Line data Source code
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 :
21 : #include "basiccharclass.hxx"
22 : #include "sbcomp.hxx"
23 :
24 : struct TokenTable { SbiToken t; const char *s; };
25 :
26 : static short nToken; // number of tokens
27 :
28 : static const TokenTable* pTokTable;
29 :
30 : static const TokenTable aTokTable_Basic [] = {
31 : { CAT, "&" },
32 : { MUL, "*" },
33 : { PLUS, "+" },
34 : { MINUS, "-" },
35 : { DIV, "/" },
36 : { EOS, ":" },
37 : { ASSIGN, ":=" },
38 : { LT, "<" },
39 : { LE, "<=" },
40 : { NE, "<>" },
41 : { EQ, "=" },
42 : { GT, ">" },
43 : { GE, ">=" },
44 : { ACCESS, "Access" },
45 : { ALIAS, "Alias" },
46 : { AND, "And" },
47 : { ANY, "Any" },
48 : { APPEND, "Append" },
49 : { AS, "As" },
50 : { ATTRIBUTE,"Attribute" },
51 : { BASE, "Base" },
52 : { BINARY, "Binary" },
53 : { TBOOLEAN, "Boolean" },
54 : { BYREF, "ByRef", },
55 : { TBYTE, "Byte", },
56 : { BYVAL, "ByVal", },
57 : { CALL, "Call" },
58 : { CASE, "Case" },
59 : { _CDECL_, "Cdecl" },
60 : { CLASSMODULE, "ClassModule" },
61 : { CLOSE, "Close" },
62 : { COMPARE, "Compare" },
63 : { COMPATIBLE,"Compatible" },
64 : { _CONST_, "Const" },
65 : { TCURRENCY,"Currency" },
66 : { TDATE, "Date" },
67 : { DECLARE, "Declare" },
68 : { DEFBOOL, "DefBool" },
69 : { DEFCUR, "DefCur" },
70 : { DEFDATE, "DefDate" },
71 : { DEFDBL, "DefDbl" },
72 : { DEFERR, "DefErr" },
73 : { DEFINT, "DefInt" },
74 : { DEFLNG, "DefLng" },
75 : { DEFOBJ, "DefObj" },
76 : { DEFSNG, "DefSng" },
77 : { DEFSTR, "DefStr" },
78 : { DEFVAR, "DefVar" },
79 : { DIM, "Dim" },
80 : { DO, "Do" },
81 : { TDOUBLE, "Double" },
82 : { EACH, "Each" },
83 : { ELSE, "Else" },
84 : { ELSEIF, "ElseIf" },
85 : { END, "End" },
86 : { ENDENUM, "End Enum" },
87 : { ENDFUNC, "End Function" },
88 : { ENDIF, "End If" },
89 : { ENDPROPERTY, "End Property" },
90 : { ENDSELECT,"End Select" },
91 : { ENDSUB, "End Sub" },
92 : { ENDTYPE, "End Type" },
93 : { ENDIF, "EndIf" },
94 : { ENUM, "Enum" },
95 : { EQV, "Eqv" },
96 : { ERASE, "Erase" },
97 : { _ERROR_, "Error" },
98 : { EXIT, "Exit" },
99 : { BASIC_EXPLICIT, "Explicit" },
100 : { FOR, "For" },
101 : { FUNCTION, "Function" },
102 : { GET, "Get" },
103 : { GLOBAL, "Global" },
104 : { GOSUB, "GoSub" },
105 : { GOTO, "GoTo" },
106 : { IF, "If" },
107 : { IMP, "Imp" },
108 : { IMPLEMENTS, "Implements" },
109 : { _IN_, "In" },
110 : { INPUT, "Input" }, // also INPUT #
111 : { TINTEGER, "Integer" },
112 : { IS, "Is" },
113 : { LET, "Let" },
114 : { LIB, "Lib" },
115 : { LIKE, "Like" },
116 : { LINE, "Line" },
117 : { LINEINPUT,"Line Input" },
118 : { LOCAL, "Local" },
119 : { LOCK, "Lock" },
120 : { TLONG, "Long" },
121 : { LOOP, "Loop" },
122 : { LPRINT, "LPrint" },
123 : { LSET, "LSet" }, // JSM
124 : { MOD, "Mod" },
125 : { NAME, "Name" },
126 : { NEW, "New" },
127 : { NEXT, "Next" },
128 : { NOT, "Not" },
129 : { TOBJECT, "Object" },
130 : { ON, "On" },
131 : { OPEN, "Open" },
132 : { OPTION, "Option" },
133 : { _OPTIONAL_, "Optional" },
134 : { OR, "Or" },
135 : { OUTPUT, "Output" },
136 : { PARAMARRAY, "ParamArray" },
137 : { PRESERVE, "Preserve" },
138 : { PRINT, "Print" },
139 : { PRIVATE, "Private" },
140 : { PROPERTY, "Property" },
141 : { PUBLIC, "Public" },
142 : { RANDOM, "Random" },
143 : { READ, "Read" },
144 : { REDIM, "ReDim" },
145 : { REM, "Rem" },
146 : { RESUME, "Resume" },
147 : { RETURN, "Return" },
148 : { RSET, "RSet" }, // JSM
149 : { SELECT, "Select" },
150 : { SET, "Set" },
151 : #ifdef SHARED
152 : #undef SHARED
153 : #define tmpSHARED
154 : #endif
155 : { SHARED, "Shared" },
156 : #ifdef tmpSHARED
157 : #define SHARED
158 : #undef tmpSHARED
159 : #endif
160 : { TSINGLE, "Single" },
161 : { STATIC, "Static" },
162 : { STEP, "Step" },
163 : { STOP, "Stop" },
164 : { TSTRING, "String" },
165 : { SUB, "Sub" },
166 : { STOP, "System" },
167 : { TEXT, "Text" },
168 : { THEN, "Then" },
169 : { TO, "To", },
170 : { TYPE, "Type" },
171 : { TYPEOF, "TypeOf" },
172 : { UNTIL, "Until" },
173 : { TVARIANT, "Variant" },
174 : { VBASUPPORT, "VbaSupport" },
175 : { WEND, "Wend" },
176 : { WHILE, "While" },
177 : { WITH, "With" },
178 : { WITHEVENTS, "WithEvents" },
179 : { WRITE, "Write" }, // also WRITE #
180 : { XOR, "Xor" },
181 : { NIL, "" }
182 : };
183 :
184 :
185 : // #i109076
186 798 : TokenLabelInfo::TokenLabelInfo( void )
187 : {
188 798 : m_pTokenCanBeLabelTab = new bool[VBASUPPORT+1];
189 212268 : for( int i = 0 ; i <= VBASUPPORT ; ++i )
190 : {
191 211470 : m_pTokenCanBeLabelTab[i] = false;
192 : }
193 : // Token accepted as label by VBA
194 : SbiToken eLabelToken[] = { ACCESS, ALIAS, APPEND, BASE, BINARY, CLASSMODULE,
195 : COMPARE, COMPATIBLE, DEFERR, _ERROR_, BASIC_EXPLICIT, LIB, LINE, LPRINT, NAME,
196 798 : TOBJECT, OUTPUT, PROPERTY, RANDOM, READ, STEP, STOP, TEXT, VBASUPPORT, NIL };
197 798 : SbiToken* pTok = eLabelToken;
198 : SbiToken eTok;
199 19950 : for( pTok = eLabelToken ; (eTok = *pTok) != NIL ; ++pTok )
200 : {
201 19152 : m_pTokenCanBeLabelTab[eTok] = true;
202 : }
203 798 : }
204 :
205 5240 : TokenLabelInfo::~TokenLabelInfo()
206 : {
207 5240 : delete[] m_pTokenCanBeLabelTab;
208 5240 : }
209 :
210 :
211 : // the constructor detects the length of the token table
212 :
213 798 : SbiTokenizer::SbiTokenizer( const OUString& rSrc, StarBASIC* pb )
214 : : SbiScanner(rSrc, pb)
215 : , eCurTok(NIL)
216 : , ePush(NIL)
217 : , nPLine(0)
218 : , nPCol1(0)
219 : , nPCol2(0)
220 : , bEof(false)
221 : , bEos(true)
222 : , bKeywords(true)
223 : , bAs(false)
224 798 : , bErrorIsSymbol(true)
225 : {
226 798 : pTokTable = aTokTable_Basic;
227 798 : if( !nToken )
228 : {
229 : const TokenTable *tp;
230 72 : for( nToken = 0, tp = pTokTable; tp->t; nToken++, tp++ )
231 : {}
232 : }
233 798 : }
234 :
235 5240 : SbiTokenizer::~SbiTokenizer()
236 : {
237 5240 : }
238 :
239 :
240 6700 : void SbiTokenizer::Push( SbiToken t )
241 : {
242 6700 : if( ePush != NIL )
243 0 : Error( SbERR_INTERNAL_ERROR, "PUSH" );
244 6700 : else ePush = t;
245 6700 : }
246 :
247 0 : void SbiTokenizer::Error( SbError code, const char* pMsg )
248 : {
249 0 : aError = OUString::createFromAscii( pMsg );
250 0 : Error( code );
251 0 : }
252 :
253 0 : void SbiTokenizer::Error( SbError code, const OUString &aMsg )
254 : {
255 0 : aError = aMsg;
256 0 : Error( code );
257 0 : }
258 :
259 0 : void SbiTokenizer::Error( SbError code, SbiToken tok )
260 : {
261 0 : aError = Symbol( tok );
262 0 : Error( code );
263 0 : }
264 :
265 : // reading in the next token without absorbing it
266 :
267 437820 : SbiToken SbiTokenizer::Peek()
268 : {
269 437820 : if( ePush == NIL )
270 : {
271 103218 : sal_uInt16 nOldLine = nLine;
272 103218 : sal_uInt16 nOldCol1 = nCol1;
273 103218 : sal_uInt16 nOldCol2 = nCol2;
274 103218 : ePush = Next();
275 103218 : nPLine = nLine; nLine = nOldLine;
276 103218 : nPCol1 = nCol1; nCol1 = nOldCol1;
277 103218 : nPCol2 = nCol2; nCol2 = nOldCol2;
278 : }
279 437820 : return eCurTok = ePush;
280 : }
281 :
282 : // For decompilation. Numbers and symbols return an empty string.
283 :
284 110 : const OUString& SbiTokenizer::Symbol( SbiToken t )
285 : {
286 : // character token?
287 110 : if( t < FIRSTKWD )
288 : {
289 0 : aSym = OUString(sal::static_int_cast<sal_Unicode>(t));
290 0 : return aSym;
291 : }
292 110 : switch( t )
293 : {
294 : case NEG :
295 0 : aSym = "-";
296 0 : return aSym;
297 : case EOS :
298 0 : aSym = ":/CRLF";
299 0 : return aSym;
300 : case EOLN :
301 0 : aSym = "CRLF";
302 0 : return aSym;
303 : default:
304 110 : break;
305 : }
306 110 : const TokenTable* tp = pTokTable;
307 15070 : for( short i = 0; i < nToken; i++, tp++ )
308 : {
309 14972 : if( tp->t == t )
310 : {
311 12 : aSym = OStringToOUString(tp->s, RTL_TEXTENCODING_ASCII_US);
312 12 : return aSym;
313 : }
314 : }
315 98 : const sal_Unicode *p = aSym.getStr();
316 98 : if (*p <= ' ')
317 : {
318 0 : aSym = "???";
319 : }
320 98 : return aSym;
321 : }
322 :
323 : // Reading in the next token and put it down.
324 : // Tokens that don't appear in the token table
325 : // are directly returned as a character.
326 : // Some words are treated in a special way.
327 :
328 331816 : SbiToken SbiTokenizer::Next()
329 : {
330 331816 : if (bEof)
331 : {
332 0 : return EOLN;
333 : }
334 : // have read in one already?
335 331816 : if( ePush != NIL )
336 : {
337 109526 : eCurTok = ePush;
338 109526 : ePush = NIL;
339 109526 : nLine = nPLine;
340 109526 : nCol1 = nPCol1;
341 109526 : nCol2 = nPCol2;
342 109526 : bEos = IsEoln( eCurTok );
343 109526 : return eCurTok;
344 : }
345 : const TokenTable *tp;
346 :
347 222290 : if( !NextSym() )
348 : {
349 798 : bEof = bEos = true;
350 798 : return eCurTok = EOLN;
351 : }
352 :
353 221492 : if( aSym.startsWith("\n") )
354 : {
355 33130 : bEos = true;
356 33130 : return eCurTok = EOLN;
357 : }
358 188362 : bEos = false;
359 :
360 188362 : if( bNumber )
361 : {
362 8058 : return eCurTok = NUMBER;
363 : }
364 180304 : else if( ( eScanType == SbxDATE || eScanType == SbxSTRING ) && !bSymbol )
365 : {
366 13364 : return eCurTok = FIXSTRING;
367 : }
368 166940 : else if( aSym.isEmpty() )
369 : {
370 : //something went wrong
371 0 : bEof = bEos = true;
372 0 : return eCurTok = EOLN;
373 : }
374 : // Special cases of characters that are between "Z" and "a". ICompare()
375 : // evaluates the position of these characters in different ways.
376 166940 : else if( aSym[0] == '^' )
377 : {
378 0 : return eCurTok = EXPON;
379 : }
380 166940 : else if( aSym[0] == '\\' )
381 : {
382 0 : return eCurTok = IDIV;
383 : }
384 : else
385 : {
386 166940 : if( eScanType != SbxVARIANT
387 165690 : || ( !bKeywords && bSymbol ) )
388 1250 : return eCurTok = SYMBOL;
389 : // valid token?
390 165690 : short lb = 0;
391 165690 : short ub = nToken-1;
392 : short delta;
393 1273266 : do
394 : {
395 1330312 : delta = (ub - lb) >> 1;
396 1330312 : tp = &pTokTable[ lb + delta ];
397 1330312 : sal_Int32 res = aSym.compareToIgnoreAsciiCaseAscii( tp->s );
398 :
399 1330312 : if( res == 0 )
400 : {
401 57046 : goto special;
402 : }
403 1273266 : if( res < 0 )
404 : {
405 800646 : if ((ub - lb) == 2)
406 : {
407 109884 : ub = lb;
408 : }
409 : else
410 : {
411 690762 : ub = ub - delta;
412 : }
413 : }
414 : else
415 : {
416 472620 : if ((ub -lb) == 2)
417 : {
418 2974 : lb = ub;
419 : }
420 : else
421 : {
422 469646 : lb = lb + delta;
423 : }
424 : }
425 : }
426 : while( delta );
427 : // Symbol? if not >= token
428 108644 : sal_Unicode ch = aSym[0];
429 108644 : if( !theBasicCharClass::get().isAlpha( ch, bCompatible ) && !bSymbol )
430 : {
431 49494 : return eCurTok = (SbiToken) (ch & 0x00FF);
432 : }
433 59150 : return eCurTok = SYMBOL;
434 : }
435 : special:
436 : // #i92642
437 57046 : bool bStartOfLine = (eCurTok == NIL || eCurTok == REM || eCurTok == EOLN);
438 57046 : if( !bStartOfLine && (tp->t == NAME || tp->t == LINE) )
439 : {
440 282 : return eCurTok = SYMBOL;
441 : }
442 56764 : else if( tp->t == TEXT )
443 : {
444 144 : return eCurTok = SYMBOL;
445 : }
446 : // maybe we can expand this for other statements that have parameters
447 : // that are keywords ( and those keywords are only used within such
448 : // statements )
449 : // what's happening here is that if we come across 'append' ( and we are
450 : // not in the middle of parsing a special statement ( like 'Open')
451 : // we just treat keyword 'append' as a normal 'SYMBOL'.
452 : // Also we accept Dim APPEND
453 56620 : else if ( ( !bInStatement || eCurTok == DIM ) && tp->t == APPEND )
454 : {
455 0 : return eCurTok = SYMBOL;
456 : }
457 : // #i92642: Special LINE token handling -> SbiParser::Line()
458 :
459 : // END IF, CASE, SUB, DEF, FUNCTION, TYPE, CLASS, WITH
460 56620 : if( tp->t == END )
461 : {
462 : // from 15.3.96, special treatment for END, at Peek() the current
463 : // time is lost, so memorize everything and restore after
464 2970 : sal_uInt16 nOldLine = nLine;
465 2970 : sal_uInt16 nOldCol = nCol;
466 2970 : sal_uInt16 nOldCol1 = nCol1;
467 2970 : sal_uInt16 nOldCol2 = nCol2;
468 2970 : OUString aOldSym = aSym;
469 2970 : SaveLine(); // save pLine in the scanner
470 :
471 2970 : eCurTok = Peek();
472 2970 : switch( eCurTok )
473 : {
474 1114 : case IF: Next(); eCurTok = ENDIF; break;
475 32 : case SELECT: Next(); eCurTok = ENDSELECT; break;
476 1146 : case SUB: Next(); eCurTok = ENDSUB; break;
477 552 : case FUNCTION: Next(); eCurTok = ENDFUNC; break;
478 0 : case PROPERTY: Next(); eCurTok = ENDPROPERTY; break;
479 4 : case TYPE: Next(); eCurTok = ENDTYPE; break;
480 0 : case ENUM: Next(); eCurTok = ENDENUM; break;
481 78 : case WITH: Next(); eCurTok = ENDWITH; break;
482 44 : default : eCurTok = END; break;
483 : }
484 2970 : nCol1 = nOldCol1;
485 2970 : if( eCurTok == END )
486 : {
487 : // reset everything so that token is read completely newly after END
488 44 : ePush = NIL;
489 44 : nLine = nOldLine;
490 44 : nCol = nOldCol;
491 44 : nCol2 = nOldCol2;
492 44 : aSym = aOldSym;
493 44 : RestoreLine();
494 : }
495 2970 : return eCurTok;
496 : }
497 : // are data types keywords?
498 : // there is ERROR(), DATA(), STRING() etc.
499 53650 : eCurTok = tp->t;
500 : // AS: data types are keywords
501 53650 : if( tp->t == AS )
502 : {
503 4120 : bAs = true;
504 : }
505 : else
506 : {
507 49530 : if( bAs )
508 : {
509 4102 : bAs = false;
510 : }
511 45428 : else if( eCurTok >= DATATYPE1 && eCurTok <= DATATYPE2 && (bErrorIsSymbol || eCurTok != _ERROR_) )
512 : {
513 262 : eCurTok = SYMBOL;
514 : }
515 : }
516 :
517 : // CLASSMODULE, PROPERTY, GET, ENUM token only visible in compatible mode
518 53650 : SbiToken eTok = tp->t;
519 53650 : if( bCompatible )
520 : {
521 : // #129904 Suppress system
522 41208 : if( eTok == STOP && aSym.equalsIgnoreAsciiCase("system") )
523 : {
524 0 : eCurTok = SYMBOL;
525 : }
526 41208 : if( eTok == GET && bStartOfLine )
527 : {
528 0 : eCurTok = SYMBOL;
529 : }
530 : }
531 : else
532 : {
533 12442 : if( eTok == CLASSMODULE ||
534 12442 : eTok == IMPLEMENTS ||
535 12442 : eTok == PARAMARRAY ||
536 12442 : eTok == ENUM ||
537 12442 : eTok == PROPERTY ||
538 12442 : eTok == GET ||
539 : eTok == TYPEOF )
540 : {
541 0 : eCurTok = SYMBOL;
542 : }
543 : }
544 :
545 53650 : bEos = IsEoln( eCurTok );
546 53650 : return eCurTok;
547 : }
548 :
549 12646 : bool SbiTokenizer::MayBeLabel( bool bNeedsColon )
550 : {
551 12646 : if( eCurTok == SYMBOL || m_aTokenLabelInfo.canTokenBeLabel( eCurTok ) )
552 : {
553 6428 : return bNeedsColon ? DoesColonFollow() : true;
554 : }
555 : else
556 : {
557 6218 : return ( eCurTok == NUMBER
558 2 : && eScanType == SbxINTEGER
559 6220 : && nVal >= 0 );
560 : }
561 : }
562 :
563 :
564 0 : OUString SbiTokenizer::GetKeywordCase( const OUString& sKeyword )
565 : {
566 0 : if( !nToken )
567 : {
568 : const TokenTable *tp;
569 0 : for( nToken = 0, tp = pTokTable; tp->t; nToken++, tp++ )
570 : {}
571 : }
572 0 : const TokenTable* tp = pTokTable;
573 0 : for( short i = 0; i < nToken; i++, tp++ )
574 : {
575 0 : OUString sStr = OStringToOUString(tp->s, RTL_TEXTENCODING_ASCII_US);
576 0 : if( sStr.equalsIgnoreAsciiCase(sKeyword) )
577 : {
578 0 : return sStr;
579 : }
580 0 : }
581 0 : return OUString("");
582 1203 : }
583 :
584 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|