1 // //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** $Id: vc_lexer.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //** Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //** This program is free software; you can redistribute it and/or
15 //** modify it under the terms of the GNU General Public License
16 //** as published by the Free Software Foundation; either version 2
17 //** of the License, or (at your option) any later version.
18 //**
19 //** This program is distributed in the hope that it will be useful,
20 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 //** GNU General Public License for more details.
23 //**
24 //**************************************************************************
25
26 // HEADER FILES ------------------------------------------------------------
27
28 #include "vc_local.h"
29
30 // MACROS ------------------------------------------------------------------
31
32 // TYPES -------------------------------------------------------------------
33
34 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
35
36 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
37
38 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
39
40 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
41
42 // PUBLIC DATA DEFINITIONS -------------------------------------------------
43
44 const char* VLexer::TokenNames[] =
45 {
46 "",
47 "END OF FILE",
48 "IDENTIFIER",
49 "NAME LITERAL",
50 "STRING LITERAL",
51 "INTEGER LITERAL",
52 "FLOAT LITERAL",
53 // Keywords
54 "abstract",
55 "array",
56 "bool",
57 "break",
58 "byte",
59 "case",
60 "class",
61 "const",
62 "continue",
63 "decorate",
64 "default",
65 "defaultproperties",
66 "delegate",
67 "do",
68 "else",
69 "enum",
70 "false",
71 "final",
72 "float",
73 "for",
74 "foreach",
75 "game",
76 "get",
77 "if",
78 "import",
79 "int",
80 "iterator",
81 "name",
82 "native",
83 "none",
84 "NULL",
85 "optional",
86 "out",
87 "private",
88 "readonly",
89 "reliable",
90 "replication",
91 "return",
92 "self",
93 "set",
94 "spawner",
95 "state",
96 "states",
97 "static",
98 "string",
99 "struct",
100 "switch",
101 "transient",
102 "true",
103 "unreliable",
104 "vector",
105 "void",
106 "while",
107 "__mobjinfo__",
108 "__scriptid__",
109 // Punctuation
110 "...",
111 "<<=",
112 ">>=",
113 "+=",
114 "-=",
115 "*=",
116 "/=",
117 "%=",
118 "&=",
119 "|=",
120 "^=",
121 "==",
122 "!=",
123 "<=",
124 ">=",
125 "&&",
126 "||",
127 "<<",
128 ">>",
129 "++",
130 "--",
131 "->",
132 "::",
133 "<",
134 ">",
135 "?",
136 "&",
137 "|",
138 "^",
139 "~",
140 "!",
141 "+",
142 "-",
143 "*",
144 "/",
145 "%",
146 "(",
147 ")",
148 ".",
149 ",",
150 ";",
151 ":",
152 "=",
153 "[",
154 "]",
155 "{",
156 "}",
157 };
158
159 // PRIVATE DATA DEFINITIONS ------------------------------------------------
160
161 // CODE --------------------------------------------------------------------
162
163 //==========================================================================
164 //
165 // VLexer::VLexer
166 //
167 //==========================================================================
168
VLexer()169 VLexer::VLexer()
170 : SourceOpen(false)
171 , Src(NULL)
172 , Token(TK_NoToken)
173 , Number(0)
174 , Float(0)
175 , Name(NAME_None)
176 {
177 int i;
178
179 memset(TokenStringBuffer, 0, sizeof(TokenStringBuffer));
180
181 for (i = 0; i < 256; i++)
182 {
183 ASCIIToChrCode[i] = CHR_Special;
184 ASCIIToHexDigit[i] = NON_HEX_DIGIT;
185 }
186 for (i = '0'; i <= '9'; i++)
187 {
188 ASCIIToChrCode[i] = CHR_Number;
189 ASCIIToHexDigit[i] = i-'0';
190 }
191 for (i = 'A'; i <= 'F'; i++)
192 {
193 ASCIIToHexDigit[i] = 10+(i-'A');
194 }
195 for (i = 'a'; i <= 'f'; i++)
196 {
197 ASCIIToHexDigit[i] = 10+(i-'a');
198 }
199 for (i = 'A'; i <= 'Z'; i++)
200 {
201 ASCIIToChrCode[i] = CHR_Letter;
202 }
203 for (i = 'a'; i <= 'z'; i++)
204 {
205 ASCIIToChrCode[i] = CHR_Letter;
206 }
207 ASCIIToChrCode[(int)'\"'] = CHR_Quote;
208 ASCIIToChrCode[(int)'\''] = CHR_SingleQuote;
209 ASCIIToChrCode[(int)'_'] = CHR_Letter;
210 ASCIIToChrCode[0] = CHR_EOF;
211 ASCIIToChrCode[EOF_CHARACTER] = CHR_EOF;
212 String = TokenStringBuffer;
213 }
214
215 //==========================================================================
216 //
217 // VLexer::OpenSource
218 //
219 //==========================================================================
220
OpenSource(const VStr & FileName)221 void VLexer::OpenSource(const VStr& FileName)
222 {
223 // Read file and prepare for compilation.
224 PushSource(Location, FileName);
225
226 SourceOpen = true;
227
228 Token = TK_NoToken;
229 }
230
231 //==========================================================================
232 //
233 // VLexer::~VLexer
234 //
235 //==========================================================================
236
~VLexer()237 VLexer::~VLexer()
238 {
239 while (Src)
240 {
241 PopSource();
242 }
243 SourceOpen = false;
244 }
245
246 //==========================================================================
247 //
248 // VLexer::PushSource
249 //
250 //==========================================================================
251
PushSource(TLocation & Loc,const VStr & FileName)252 void VLexer::PushSource(TLocation& Loc, const VStr& FileName)
253 {
254 #ifdef IN_VCC
255 VStream* Strm = OpenFile(FileName);
256 #else
257 VStream* Strm = FL_OpenFileRead(FileName);
258 #endif
259 if (!Strm)
260 {
261 FatalError("Couldn't open %s", *FileName);
262 return;
263 }
264
265 VSourceFile* NewSrc = new VSourceFile();
266 NewSrc->Next = Src;
267 Src = NewSrc;
268
269 // Copy file name
270 NewSrc->FileName = FileName;
271
272 // Extract path to the file.
273 const char* PathEnd = *FileName + FileName.Length() - 1;
274 while (PathEnd >= *FileName && *PathEnd != '/' && *PathEnd != '\\')
275 {
276 PathEnd--;
277 }
278 if (PathEnd >= *FileName)
279 {
280 NewSrc->Path = VStr(FileName, 0, (PathEnd - *FileName) + 1);
281 }
282
283 // Read the file
284 int FileSize = Strm->TotalSize();
285 NewSrc->FileStart = new char[FileSize + 1];
286 Strm->Serialise(NewSrc->FileStart, FileSize);
287 Strm->Close();
288 delete Strm;
289 Strm = NULL;
290 NewSrc->FileStart[FileSize] = 0;
291 NewSrc->FileEnd = NewSrc->FileStart + FileSize;
292 NewSrc->FilePtr = NewSrc->FileStart;
293
294 // Skip garbage some editors add in the begining of UTF-8 files.
295 if ((vuint8)NewSrc->FilePtr[0] == 0xef &&
296 (vuint8)NewSrc->FilePtr[1] == 0xbb &&
297 (vuint8)NewSrc->FilePtr[2] == 0xbf)
298 {
299 NewSrc->FilePtr += 3;
300 }
301
302 // Save current character and location to be able to restore them.
303 NewSrc->Chr = Chr;
304 NewSrc->Loc = Location;
305
306 NewSrc->SourceIdx = TLocation::AddSourceFile(FileName);
307 NewSrc->Line = 1;
308 NewSrc->IncLineNumber = false;
309 NewSrc->NewLine = true;
310 NewSrc->Skipping = false;
311 Location = TLocation(NewSrc->SourceIdx, NewSrc->Line);
312 NextChr();
313 }
314
315 //==========================================================================
316 //
317 // VLexer::PopSource
318 //
319 //==========================================================================
320
PopSource()321 void VLexer::PopSource()
322 {
323 if (!Src)
324 {
325 return;
326 }
327
328 if (Src->IfStates.Num())
329 {
330 ParseError(Location, "#ifdef without a corresponding #endif");
331 }
332
333 VSourceFile* Tmp = Src;
334 delete[] Tmp->FileStart;
335 Tmp->FileStart = NULL;
336 Src = Tmp->Next;
337 Chr = Tmp->Chr;
338 Location = Tmp->Loc;
339 delete Tmp;
340 Tmp = NULL;
341 }
342
343 //==========================================================================
344 //
345 // VLexer::NextToken
346 //
347 //==========================================================================
348
NextToken()349 void VLexer::NextToken()
350 {
351 NewLine = Src->NewLine;
352 do
353 {
354 TokenStringBuffer[0] = 0;
355 SkipWhitespaceAndComments();
356 if (Src->NewLine)
357 {
358 NewLine = true;
359 // A new line has been started, check preprocessor directive.
360 Src->NewLine = false;
361 if (Chr == '#')
362 {
363 ProcessPreprocessor();
364 continue;
365 }
366 }
367 switch (ASCIIToChrCode[(vuint8)Chr])
368 {
369 case CHR_EOF:
370 PopSource();
371 if (Src)
372 {
373 Token = TK_NoToken;
374 }
375 else
376 {
377 Token = TK_EOF;
378 }
379 break;
380 case CHR_Letter:
381 ProcessLetterToken(true);
382 break;
383 case CHR_Number:
384 ProcessNumberToken();
385 break;
386 case CHR_Quote:
387 ProcessQuoteToken();
388 break;
389 case CHR_SingleQuote:
390 ProcessSingleQuoteToken();
391 break;
392 default:
393 ProcessSpecialToken();
394 break;
395 }
396 if (Token != TK_EOF && Src->Skipping)
397 {
398 Token = TK_NoToken;
399 }
400 } while (Token == TK_NoToken);
401 }
402
403 //==========================================================================
404 //
405 // VLexer::NextChr
406 //
407 //==========================================================================
408
NextChr()409 void VLexer::NextChr()
410 {
411 if (Src->FilePtr >= Src->FileEnd)
412 {
413 Chr = EOF_CHARACTER;
414 return;
415 }
416 if (Src->IncLineNumber)
417 {
418 Src->Line++;
419 Location = TLocation(Src->SourceIdx, Src->Line);
420 Src->IncLineNumber = false;
421 }
422 Chr = *Src->FilePtr++;
423 if ((vuint8)Chr < ' ')
424 {
425 if (Chr == '\n')
426 {
427 Src->IncLineNumber = true;
428 Src->NewLine = true;
429 }
430 Chr = ' ';
431 }
432 }
433
434 //==========================================================================
435 //
436 // VLexer::SkipWhitespaceAndComments
437 //
438 //==========================================================================
439
SkipWhitespaceAndComments()440 void VLexer::SkipWhitespaceAndComments()
441 {
442 bool Done;
443 do
444 {
445 Done = true;
446 while (Chr == ' ') NextChr();
447 if (Chr == '/' && *Src->FilePtr == '*')
448 {
449 // Block comment
450 NextChr();
451 do
452 {
453 NextChr();
454 if (Chr == EOF_CHARACTER)
455 {
456 ParseError(Location, "End of file inside a comment");
457 return;
458 }
459 } while (Chr != '*' || *Src->FilePtr != '/');
460 NextChr();
461 NextChr();
462 Done = false;
463 }
464 else if (Chr == '/' && *Src->FilePtr == '/')
465 {
466 // C++ style comment
467 NextChr();
468 do
469 {
470 NextChr();
471 if (Chr == EOF_CHARACTER)
472 {
473 ParseError(Location, "End of file inside a comment");
474 return;
475 }
476 } while (!Src->IncLineNumber);
477 Done = false;
478 }
479 } while (!Done);
480 }
481
482 //==========================================================================
483 //
484 // VLexer::ProcessPreprocessor
485 //
486 //==========================================================================
487
ProcessPreprocessor()488 void VLexer::ProcessPreprocessor()
489 {
490 NextChr();
491 if (Src->NewLine || Chr == EOF_CHARACTER)
492 {
493 ParseError(Location, "Bad directive.");
494 return;
495 }
496 if (ASCIIToChrCode[(vuint8)Chr] != CHR_Letter)
497 {
498 ParseError(Location, "Bad directive.");
499 while (!Src->NewLine && Chr != EOF_CHARACTER) NextChr();
500 return;
501 }
502
503 ProcessLetterToken(false);
504 if (!VStr::Cmp(TokenStringBuffer, "line"))
505 {
506 // Read line number
507 SkipWhitespaceAndComments();
508 if (ASCIIToChrCode[(vuint8)Chr] != CHR_Number)
509 {
510 ParseError(Location, "Bad directive.");
511 }
512 ProcessNumberToken();
513 Src->Line = Number - 1;
514
515 // Read file name
516 SkipWhitespaceAndComments();
517 if (ASCIIToChrCode[(vuint8)Chr] != CHR_Quote)
518 {
519 ParseError(Location, "Bad directive.");
520 }
521 ProcessFileName();
522 Src->SourceIdx = TLocation::AddSourceFile(String);
523 Location = TLocation(Src->SourceIdx, Src->Line);
524
525 // Ignore flags
526 while (!Src->NewLine)
527 {
528 NextChr();
529 }
530 }
531 else if (!VStr::Cmp(TokenStringBuffer, "define"))
532 {
533 ProcessDefine();
534 }
535 else if (!VStr::Cmp(TokenStringBuffer, "ifdef"))
536 {
537 ProcessIf(true);
538 }
539 else if (!VStr::Cmp(TokenStringBuffer, "ifndef"))
540 {
541 ProcessIf(false);
542 }
543 else if (!VStr::Cmp(TokenStringBuffer, "else"))
544 {
545 ProcessElse();
546 }
547 else if (!VStr::Cmp(TokenStringBuffer, "endif"))
548 {
549 ProcessEndIf();
550 }
551 else if (!VStr::Cmp(TokenStringBuffer, "include"))
552 {
553 ProcessInclude();
554 return;
555 }
556 else
557 {
558 ParseError(Location, "Bad directive.");
559 while (!Src->NewLine && Chr != EOF_CHARACTER) NextChr();
560 }
561 Token = TK_NoToken;
562
563 SkipWhitespaceAndComments();
564 // A new-line is expected at the end of preprocessor directive.
565 if (!Src->NewLine)
566 {
567 ParseError(Location, "Bad directive.");
568 }
569 }
570
571 //==========================================================================
572 //
573 // VLexer::ProcessDefine
574 //
575 //==========================================================================
576
ProcessDefine()577 void VLexer::ProcessDefine()
578 {
579 SkipWhitespaceAndComments();
580 // Argument to the #define must be on the same line.
581 if (Src->NewLine || Chr == EOF_CHARACTER)
582 {
583 ParseError(Location, "Bad directive.");
584 return;
585 }
586
587 // Parse name to be defined
588 if (ASCIIToChrCode[(vuint8)Chr] != CHR_Letter)
589 {
590 ParseError(Location, "Bad directive.");
591 while (!Src->NewLine && Chr != EOF_CHARACTER) NextChr();
592 return;
593 }
594 ProcessLetterToken(false);
595
596 if (Src->Skipping)
597 {
598 return;
599 }
600
601 AddDefine(TokenStringBuffer);
602 }
603
604 //==========================================================================
605 //
606 // VLexer::AddDefine
607 //
608 //==========================================================================
609
AddDefine(const VStr & CondName)610 void VLexer::AddDefine(const VStr& CondName)
611 {
612 // Check for redefined names.
613 bool Found = false;
614 for (int i = 0; i < Defines.Num(); i++)
615 {
616 if (Defines[i] == CondName)
617 {
618 ParseWarning(Location, "Redefined conditional");
619 Found = true;
620 break;
621 }
622 }
623 if (!Found)
624 {
625 // Add it.
626 Defines.Append(CondName);
627 }
628 }
629
630 //==========================================================================
631 //
632 // VLexer::ProcessIf
633 //
634 //==========================================================================
635
ProcessIf(bool OnTrue)636 void VLexer::ProcessIf(bool OnTrue)
637 {
638 SkipWhitespaceAndComments();
639 // Argument to the #ifdef must be on the same line.
640 if (Src->NewLine || Chr == EOF_CHARACTER)
641 {
642 ParseError(Location, "Bad directive.");
643 return;
644 }
645
646 // Parse condition name
647 if (ASCIIToChrCode[(vuint8)Chr] != CHR_Letter)
648 {
649 ParseError(Location, "Bad directive.");
650 while (!Src->NewLine && Chr != EOF_CHARACTER) NextChr();
651 return;
652 }
653 ProcessLetterToken(false);
654
655 if (Src->Skipping)
656 {
657 Src->IfStates.Append(IF_Skip);
658 }
659 else
660 {
661 // Check if the names has been defined.
662 bool Found = false;
663 for (int i = 0; i < Defines.Num(); i++)
664 {
665 if (Defines[i] == TokenStringBuffer)
666 {
667 Found = true;
668 break;
669 }
670 }
671 if (Found == OnTrue)
672 {
673 Src->IfStates.Append(IF_True);
674 }
675 else
676 {
677 Src->IfStates.Append(IF_False);
678 Src->Skipping = true;
679 }
680 }
681 }
682
683 //==========================================================================
684 //
685 // VLexer::ProcessElse
686 //
687 //==========================================================================
688
ProcessElse()689 void VLexer::ProcessElse()
690 {
691 if (!Src->IfStates.Num())
692 {
693 ParseError(Location, "#else without an #ifdef/#ifndef");
694 return;
695 }
696 switch (Src->IfStates[Src->IfStates.Num() - 1])
697 {
698 case IF_True:
699 Src->IfStates[Src->IfStates.Num() - 1] = IF_ElseFalse;
700 Src->Skipping = true;
701 break;
702 case IF_False:
703 Src->IfStates[Src->IfStates.Num() - 1] = IF_ElseTrue;
704 Src->Skipping = false;
705 break;
706 case IF_Skip:
707 Src->IfStates[Src->IfStates.Num() - 1] = IF_ElseSkip;
708 break;
709 case IF_ElseTrue:
710 case IF_ElseFalse:
711 case IF_ElseSkip:
712 ParseError(Location, "Multiple #else directives for a single #ifdef");
713 Src->Skipping = true;
714 break;
715 }
716 }
717
718 //==========================================================================
719 //
720 // VLexer::ProcessEndIf
721 //
722 //==========================================================================
723
ProcessEndIf()724 void VLexer::ProcessEndIf()
725 {
726 if (!Src->IfStates.Num())
727 {
728 ParseError(Location, "#endif without an #ifdef/#ifndef");
729 return;
730 }
731 Src->IfStates.RemoveIndex(Src->IfStates.Num() - 1);
732 if (Src->IfStates.Num() > 0)
733 {
734 switch (Src->IfStates[Src->IfStates.Num() - 1])
735 {
736 case IF_True:
737 case IF_ElseTrue:
738 Src->Skipping = false;
739 break;
740 case IF_False:
741 case IF_ElseFalse:
742 Src->Skipping = true;
743 break;
744 case IF_Skip:
745 case IF_ElseSkip:
746 break;
747 }
748 }
749 else
750 {
751 Src->Skipping = false;
752 }
753 }
754
755 //==========================================================================
756 //
757 // VLexer::ProcessInclude
758 //
759 //==========================================================================
760
ProcessInclude()761 void VLexer::ProcessInclude()
762 {
763 SkipWhitespaceAndComments();
764 // File name must be on the same line.
765 if (Src->NewLine || Chr == EOF_CHARACTER)
766 {
767 ParseError(Location, "Bad directive.");
768 return;
769 }
770
771 // Parse file name
772 if (Chr != '\"')
773 {
774 ParseError(Location, "Bad directive.");
775 while (!Src->NewLine && Chr != EOF_CHARACTER) NextChr();
776 return;
777 }
778 ProcessFileName();
779 TLocation Loc = Location;
780
781 Token = TK_NoToken;
782 SkipWhitespaceAndComments();
783 // A new-line is expected at the end of preprocessor directive.
784 if (!Src->NewLine)
785 {
786 ParseError(Location, "Bad directive.");
787 }
788
789 if (Src->Skipping)
790 {
791 return;
792 }
793
794 // Check if it's an absolute path location.
795 if (TokenStringBuffer[0] != '/' && TokenStringBuffer[0] != '\\')
796 {
797 // First try relative to the current source file.
798 if (Src->Path.IsNotEmpty())
799 {
800 VStr FileName = Src->Path + VStr(TokenStringBuffer);
801 #ifdef IN_VCC
802 VStream* Strm = OpenFile(FileName);
803 #else
804 VStream* Strm = FL_OpenFileRead(FileName);
805 #endif
806 if (Strm)
807 {
808 delete Strm;
809 Strm = NULL;
810 PushSource(Loc, FileName);
811 return;
812 }
813 }
814
815 for (int i = IncludePath.Num() - 1; i >= 0; i--)
816 {
817 VStr FileName = IncludePath[i] + VStr(TokenStringBuffer);
818 #ifdef IN_VCC
819 VStream* Strm = OpenFile(FileName);
820 #else
821 VStream* Strm = FL_OpenFileRead(FileName);
822 #endif
823 if (Strm)
824 {
825 delete Strm;
826 Strm = NULL;
827 PushSource(Loc, FileName);
828 return;
829 }
830 }
831 }
832
833 // Either it's relative to the current directory or absolute path.
834 PushSource(Loc, TokenStringBuffer);
835 }
836
837 //==========================================================================
838 //
839 // VLexer::AddIncludePath
840 //
841 //==========================================================================
842
AddIncludePath(const VStr & DirName)843 void VLexer::AddIncludePath(const VStr& DirName)
844 {
845 VStr Copy = DirName;
846 // Append trailing slash if needed.
847 if (!Copy.EndsWith("/") && !Copy.EndsWith("\\"))
848 {
849 Copy += '/';
850 }
851 IncludePath.Append(Copy);
852 }
853
854 //==========================================================================
855 //
856 // VLexer::ProcessNumberToken
857 //
858 //==========================================================================
859
ProcessNumberToken()860 void VLexer::ProcessNumberToken()
861 {
862 char c;
863
864 Token = TK_IntLiteral;
865 c = Chr;
866 NextChr();
867 Number = c - '0';
868 if (c == '0' && (Chr == 'x' || Chr == 'X'))
869 {
870 // Hexadecimal constant.
871 NextChr();
872 while (ASCIIToHexDigit[(vuint8)Chr] != NON_HEX_DIGIT)
873 {
874 Number = (Number << 4) + ASCIIToHexDigit[(vuint8)Chr];
875 NextChr();
876 }
877 return;
878 }
879 while (ASCIIToChrCode[(vuint8)Chr] == CHR_Number)
880 {
881 Number = 10 * Number + (Chr - '0');
882 NextChr();
883 }
884 if (Chr == '.')
885 {
886 Token = TK_FloatLiteral;
887 NextChr(); // Point
888 Float = Number;
889 float fmul = 0.1;
890 while (ASCIIToChrCode[(vuint8)Chr] == CHR_Number)
891 {
892 Float += (Chr - '0') * fmul;
893 fmul /= 10.0;
894 NextChr();
895 }
896 return;
897 }
898 if (Chr == '_')
899 {
900 int radix;
901 int digit;
902
903 NextChr(); // Underscore
904 radix = Number;
905 if (radix < 2 || radix > 36)
906 {
907 ParseError(Location, ERR_BAD_RADIX_CONSTANT);
908 radix = 2;
909 }
910 Number = 0;
911 do
912 {
913 digit = VStr::ToUpper(Chr);
914 if (digit < '0' || (digit > '9' && digit < 'A') || digit > 'Z')
915 {
916 digit = -1;
917 }
918 else if(digit > '9')
919 {
920 digit = 10 + digit - 'A';
921 }
922 else
923 {
924 digit -= '0';
925 }
926 if (digit >= radix)
927 {
928 digit = -1;
929 }
930 if (digit != -1)
931 {
932 Number = radix * Number + digit;
933 NextChr();
934 }
935 } while (digit != -1);
936 }
937 }
938
939 //==========================================================================
940 //
941 // VLexer::ProcessChar
942 //
943 //==========================================================================
944
ProcessChar()945 void VLexer::ProcessChar()
946 {
947 if (Chr == EOF_CHARACTER)
948 {
949 ParseError(Location, ERR_EOF_IN_STRING);
950 BailOut();
951 }
952 if (Src->IncLineNumber)
953 {
954 ParseError(Location, ERR_NEW_LINE_INSIDE_QUOTE);
955 }
956 if (Chr == '\\')
957 {
958 // Special symbol
959 NextChr();
960 if (Chr == EOF_CHARACTER)
961 {
962 ParseError(Location, ERR_EOF_IN_STRING);
963 BailOut();
964 }
965 if (Src->IncLineNumber)
966 {
967 ParseError(Location, ERR_NEW_LINE_INSIDE_QUOTE);
968 }
969 if (Chr == 'n')
970 Chr = '\n';
971 else if (Chr == '\'')
972 Chr = '\'';
973 else if (Chr == '"')
974 Chr = '"';
975 else if (Chr == 't')
976 Chr = '\t';
977 else if (Chr == '\\')
978 Chr = '\\';
979 else if (Chr == 'c')
980 Chr = TEXT_COLOUR_ESCAPE;
981 else
982 ParseError(Location, ERR_UNKNOWN_ESC_CHAR);
983 }
984 }
985
986 //==========================================================================
987 //
988 // VLexer::ProcessQuoteToken
989 //
990 //==========================================================================
991
ProcessQuoteToken()992 void VLexer::ProcessQuoteToken()
993 {
994 int len;
995
996 Token = TK_StringLiteral;
997 len = 0;
998 NextChr();
999 while (Chr != '\"')
1000 {
1001 if (len >= MAX_QUOTED_LENGTH - 1)
1002 {
1003 ParseError(Location, ERR_STRING_TOO_LONG);
1004 NextChr();
1005 continue;
1006 }
1007 ProcessChar();
1008 TokenStringBuffer[len] = Chr;
1009 NextChr();
1010 len++;
1011 }
1012 TokenStringBuffer[len] = 0;
1013 NextChr();
1014 }
1015
1016 //==========================================================================
1017 //
1018 // VLexer::ProcessSingleQuoteToken
1019 //
1020 //==========================================================================
1021
ProcessSingleQuoteToken()1022 void VLexer::ProcessSingleQuoteToken()
1023 {
1024 int len;
1025
1026 Token = TK_NameLiteral;
1027 len = 0;
1028 NextChr();
1029 while (Chr != '\'')
1030 {
1031 if (len >= MAX_IDENTIFIER_LENGTH - 1)
1032 {
1033 ParseError(Location, ERR_STRING_TOO_LONG);
1034 NextChr();
1035 continue;
1036 }
1037 ProcessChar();
1038 TokenStringBuffer[len] = Chr;
1039 NextChr();
1040 len++;
1041 }
1042 TokenStringBuffer[len] = 0;
1043 NextChr();
1044 Name = TokenStringBuffer;
1045 }
1046
1047 //==========================================================================
1048 //
1049 // VLexer::ProcessLetterToken
1050 //
1051 //==========================================================================
1052
ProcessLetterToken(bool CheckKeywords)1053 void VLexer::ProcessLetterToken(bool CheckKeywords)
1054 {
1055 int len;
1056
1057 Token = TK_Identifier;
1058 len = 0;
1059 while (ASCIIToChrCode[(vuint8)Chr] == CHR_Letter
1060 || ASCIIToChrCode[(vuint8)Chr] == CHR_Number)
1061 {
1062 if (len == MAX_IDENTIFIER_LENGTH - 1)
1063 {
1064 ParseError(Location, ERR_IDENTIFIER_TOO_LONG);
1065 NextChr();
1066 continue;
1067 }
1068 TokenStringBuffer[len] = Chr;
1069 len++;
1070 NextChr();
1071 }
1072 TokenStringBuffer[len] = 0;
1073
1074 if (!CheckKeywords)
1075 {
1076 return;
1077 }
1078
1079 register const char* s = TokenStringBuffer;
1080 switch (s[0])
1081 {
1082 case '_':
1083 if (s[1] == '_')
1084 {
1085 if (s[2] == 'm' && s[3] == 'o' && s[4] == 'b' && s[5] == 'j' &&
1086 s[6] == 'i' && s[7] == 'n' && s[8] == 'f' && s[9] == 'o' &&
1087 s[10] == '_' && s[11] == '_' && s[12] == 0)
1088 {
1089 Token = TK_MobjInfo;
1090 }
1091 else if (s[2] == 's' && s[3] == 'c' && s[4] == 'r' &&
1092 s[5] == 'i' && s[6] == 'p' && s[7] == 't' && s[8] == 'i' &&
1093 s[9] == 'd' && s[10] == '_' && s[11] == '_' && s[12] == 0)
1094 {
1095 Token = TK_ScriptId;
1096 }
1097 }
1098 break;
1099
1100 case 'a':
1101 if (s[1] == 'b' && s[2] == 's' && s[3] == 't' && s[4] == 'r' &&
1102 s[5] == 'a' && s[6] == 'c' && s[7] == 't' && s[8] == 0)
1103 {
1104 Token = TK_Abstract;
1105 }
1106 else if (s[1] == 'r' && s[2] == 'r' && s[3] == 'a' && s[4] == 'y' &&
1107 s[5] == 0)
1108 {
1109 Token = TK_Array;
1110 }
1111 break;
1112
1113 case 'b':
1114 if (s[1] == 'o' && s[2] == 'o' && s[3] == 'l' && s[4] == 0)
1115 {
1116 Token = TK_Bool;
1117 }
1118 else if (s[1] == 'r' && s[2] == 'e' && s[3] == 'a' && s[4] == 'k' &&
1119 s[5] == 0)
1120 {
1121 Token = TK_Break;
1122 }
1123 else if (s[1] == 'y' && s[2] == 't' && s[3] == 'e' && s[4] == 0)
1124 {
1125 Token = TK_Byte;
1126 }
1127 break;
1128
1129 case 'c':
1130 if (s[1] == 'a' && s[2] == 's' && s[3] == 'e' && s[4] == 0)
1131 {
1132 Token = TK_Case;
1133 }
1134 else if (s[1] == 'l' && s[2] == 'a' && s[3] == 's' && s[4] == 's' &&
1135 s[5] == 0)
1136 {
1137 Token = TK_Class;
1138 }
1139 else if (s[1] == 'o' && s[2] == 'n')
1140 {
1141 if (s[3] == 's' && s[4] == 't' && s[5] == 0)
1142 {
1143 Token = TK_Const;
1144 }
1145 else if (s[3] == 't' && s[4] == 'i' && s[5] == 'n' &&
1146 s[6] == 'u' && s[7] == 'e' && s[8] == 0)
1147 {
1148 Token = TK_Continue;
1149 }
1150 }
1151 break;
1152
1153 case 'd':
1154 if (s[1] == 'e')
1155 {
1156 if (s[2] == 'c' && s[3] == 'o' && s[4] == 'r' &&
1157 s[5] == 'a' && s[6] == 't' && s[7] == 'e' && s[8] == 0)
1158 {
1159 Token = TK_Decorate;
1160 }
1161 else if (s[2] == 'f' && s[3] == 'a' && s[4] == 'u' &&
1162 s[5] == 'l' && s[6] == 't')
1163 {
1164 if (s[7] == 0)
1165 {
1166 Token = TK_Default;
1167 }
1168 else if (s[7] == 'p' && s[8] == 'r' && s[9] == 'o' &&
1169 s[10] == 'p' && s[11] == 'e' && s[12] == 'r' &&
1170 s[13] == 't' && s[14] == 'i' && s[15] == 'e' &&
1171 s[16] == 's' && s[17] == 0)
1172 {
1173 Token = TK_DefaultProperties;
1174 }
1175 }
1176 else if (s[2] == 'l' && s[3] == 'e' && s[4] == 'g' &&
1177 s[5] == 'a' && s[6] == 't' && s[7] == 'e' && s[8] == 0)
1178 {
1179 Token = TK_Delegate;
1180 }
1181 }
1182 else if (s[1] == 'o' && s[2] == 0)
1183 {
1184 Token = TK_Do;
1185 }
1186 break;
1187
1188 case 'e':
1189 if (s[1] == 'l' && s[2] == 's' && s[3] == 'e' && s[4] == 0)
1190 {
1191 Token = TK_Else;
1192 }
1193 else if (s[1] == 'n' && s[2] == 'u' && s[3] == 'm' && s[4] == 0)
1194 {
1195 Token = TK_Enum;
1196 }
1197 break;
1198
1199 case 'f':
1200 if (s[1] == 'a' && s[2] == 'l' && s[3] == 's' && s[4] == 'e' &&
1201 s[5] == 0)
1202 {
1203 Token = TK_False;
1204 }
1205 else if (s[1] == 'i' && s[2] == 'n' && s[3] == 'a' && s[4] == 'l' &&
1206 s[5] == 0)
1207 {
1208 Token = TK_Final;
1209 }
1210 else if (s[1] == 'l' && s[2] == 'o' && s[3] == 'a' && s[4] == 't' &&
1211 s[5] == 0)
1212 {
1213 Token = TK_Float;
1214 }
1215 else if (s[1] == 'o' && s[2] == 'r')
1216 {
1217 if (s[3] == 0)
1218 {
1219 Token = TK_For;
1220 }
1221 else if (s[3] == 'e' && s[4] == 'a' && s[5] == 'c' &&
1222 s[6] == 'h' && s[7] == 0)
1223 {
1224 Token = TK_Foreach;
1225 }
1226 }
1227 break;
1228
1229 case 'g':
1230 if (s[1] == 'a' && s[2] == 'm' && s[3] == 'e' && s[4] == 0)
1231 {
1232 Token = TK_Game;
1233 }
1234 else if (s[1] == 'e' && s[2] == 't' && s[3] == 0)
1235 {
1236 Token = TK_Get;
1237 }
1238
1239 case 'i':
1240 if (s[1] == 'f' && s[2] == 0)
1241 {
1242 Token = TK_If;
1243 }
1244 else if (s[1] == 'm' && s[2] == 'p' && s[3] == 'o' && s[4] == 'r' &&
1245 s[5] == 't' && s[6] == 0)
1246 {
1247 Token = TK_Import;
1248 }
1249 else if (s[1] == 'n' && s[2] == 't' && s[3] == 0)
1250 {
1251 Token = TK_Int;
1252 }
1253 else if (s[1] == 't' && s[2] == 'e' && s[3] == 'r' && s[4] == 'a' &&
1254 s[5] == 't' && s[6] == 'o' && s[7] == 'r' && s[8] == 0)
1255 {
1256 Token = TK_Iterator;
1257 }
1258 break;
1259
1260 case 'n':
1261 if (s[1] == 'a')
1262 {
1263 if (s[2] == 'm' && s[3] == 'e' && s[4] == 0)
1264 {
1265 Token = TK_Name;
1266 }
1267 if (s[2] == 't' && s[3] == 'i' && s[4] == 'v' && s[5] == 'e' &&
1268 s[6] == 0)
1269 {
1270 Token = TK_Native;
1271 }
1272 }
1273 else if (s[1] == 'o' && s[2] == 'n' && s[3] == 'e' && s[4] == 0)
1274 {
1275 Token = TK_None;
1276 }
1277 break;
1278
1279 /* case 'N':
1280 if (s[1] == 'U' && s[2] == 'L' &&
1281 s[3] == 'L' && s[4] == 0)
1282 {
1283 Token = TK_KEYWORD;
1284 tk_Keyword = KW_NULL;
1285 }
1286 break;*/
1287
1288 case 'o':
1289 if (s[1] == 'p' && s[2] == 't' && s[3] == 'i' && s[4] == 'o' &&
1290 s[5] == 'n' && s[6] == 'a' && s[7] == 'l' && s[8] == 0)
1291 {
1292 Token = TK_Optional;
1293 }
1294 else if (s[1] == 'u' && s[2] == 't' && s[3] == 0)
1295 {
1296 Token = TK_Out;
1297 }
1298 break;
1299
1300 case 'p':
1301 if (s[1] == 'r' && s[2] == 'i' && s[3] == 'v' && s[4] == 'a' &&
1302 s[5] == 't' && s[6] == 'e' && s[7] == 0)
1303 {
1304 Token = TK_Private;
1305 }
1306 break;
1307
1308 case 'r':
1309 if (s[1] == 'e')
1310 {
1311 if (s[2] == 'a' && s[3] == 'd' && s[4] == 'o' && s[5] == 'n' &&
1312 s[6] == 'l' && s[7] == 'y' && s[8] == 0)
1313 {
1314 Token = TK_ReadOnly;
1315 }
1316 else if (s[2] == 'l' && s[3] == 'i' && s[4] == 'a' &&
1317 s[5] == 'b' && s[6] == 'l' && s[7] == 'e' && s[8] == 0)
1318 {
1319 Token = TK_Reliable;
1320 }
1321 else if (s[2] == 'p' && s[3] == 'l' && s[4] == 'i' &&
1322 s[5] == 'c' && s[6] == 'a' && s[7] == 't' && s[8] == 'i' &&
1323 s[9] == 'o' && s[10] == 'n' && s[11] == 0)
1324 {
1325 Token = TK_Replication;
1326 }
1327 else if (s[2] == 't' && s[3] == 'u' && s[4] == 'r' &&
1328 s[5] == 'n' && s[6] == 0)
1329 {
1330 Token = TK_Return;
1331 }
1332 }
1333 break;
1334
1335 case 's':
1336 if (s[1] == 'e')
1337 {
1338 if (s[2] == 'l' && s[3] == 'f' && s[4] == 0)
1339 {
1340 Token = TK_Self;
1341 }
1342 else if (s[2] == 't' && s[3] == 0)
1343 {
1344 Token = TK_Set;
1345 }
1346 }
1347 else if (s[1] == 'p' && s[2] == 'a' && s[3] == 'w' && s[4] == 'n' &&
1348 s[5] == 'e' && s[6] == 'r' && s[7] == 0)
1349 {
1350 Token = TK_Spawner;
1351 }
1352 else if (s[1] == 't')
1353 {
1354 if (s[2] == 'a' && s[3] == 't')
1355 {
1356 if (s[4] == 'e')
1357 {
1358 if (s[5] == 0)
1359 {
1360 Token = TK_State;
1361 }
1362 else if (s[5] == 's' && s[6] == 0)
1363 {
1364 Token = TK_States;
1365 }
1366 }
1367 else if (s[4] == 'i' && s[5] == 'c' && s[6] == 0)
1368 {
1369 Token = TK_Static;
1370 }
1371 }
1372 else if (s[2] == 'r')
1373 {
1374 if (s[3] == 'i' && s[4] == 'n' && s[5] == 'g' && s[6] == 0)
1375 {
1376 Token = TK_String;
1377 }
1378 else if (s[3] == 'u' && s[4] == 'c' && s[5] == 't' &&
1379 s[6] == 0)
1380 {
1381 Token = TK_Struct;
1382 }
1383 }
1384 }
1385 else if (s[1] == 'w' && s[2] == 'i' && s[3] == 't' && s[4] == 'c' &&
1386 s[5] == 'h' && s[6] == 0)
1387 {
1388 Token = TK_Switch;
1389 }
1390 break;
1391
1392 case 't':
1393 if (s[1] == 'r')
1394 {
1395 if (s[2] == 'a' && s[3] == 'n' && s[4] == 's' && s[5] == 'i' &&
1396 s[6] == 'e' && s[7] == 'n' && s[8] == 't' && s[9] == 0)
1397 {
1398 Token = TK_Transient;
1399 }
1400 else if (s[2] == 'u' && s[3] == 'e' && s[4] == 0)
1401 {
1402 Token = TK_True;
1403 }
1404 }
1405 break;
1406
1407 case 'u':
1408 if (s[1] == 'n' && s[2] == 'r' && s[3] == 'e' && s[4] == 'l' &&
1409 s[5] == 'i' && s[6] == 'a' && s[7] == 'b' && s[8] == 'l' &&
1410 s[9] == 'e' && s[10] == 0)
1411 {
1412 Token = TK_Unreliable;
1413 }
1414 break;
1415
1416 case 'v':
1417 if (s[1] == 'e' && s[2] == 'c' && s[3] == 't' && s[4] == 'o' &&
1418 s[5] == 'r' && s[6] == 0)
1419 {
1420 Token = TK_Vector;
1421 }
1422 else if (s[1] == 'o' && s[2] == 'i' && s[3] == 'd' && s[4] == 0)
1423 {
1424 Token = TK_Void;
1425 }
1426 break;
1427
1428 case 'w':
1429 if (s[1] == 'h' && s[2] == 'i' && s[3] == 'l' && s[4] == 'e' &&
1430 s[5] == 0)
1431 {
1432 Token = TK_While;
1433 }
1434 break;
1435 }
1436 if (s[0] == 'N' && s[1] == 'U' && s[2] == 'L' && s[3] == 'L' && s[4] == 0)
1437 {
1438 Token = TK_Null;
1439 }
1440
1441 if (Token == TK_Identifier)
1442 {
1443 Name = TokenStringBuffer;
1444 }
1445 }
1446
1447 //==========================================================================
1448 //
1449 // VLexer::ProcessSpecialToken
1450 //
1451 //==========================================================================
1452
ProcessSpecialToken()1453 void VLexer::ProcessSpecialToken()
1454 {
1455 char c = Chr;
1456 NextChr();
1457 switch (c)
1458 {
1459 case '+':
1460 if (Chr == '=')
1461 {
1462 Token = TK_AddAssign;
1463 NextChr();
1464 }
1465 else if (Chr == '+')
1466 {
1467 Token = TK_Inc;
1468 NextChr();
1469 }
1470 else
1471 {
1472 Token = TK_Plus;
1473 }
1474 break;
1475
1476 case '-':
1477 if (Chr == '=')
1478 {
1479 Token = TK_MinusAssign;
1480 NextChr();
1481 }
1482 else if (Chr == '-')
1483 {
1484 Token = TK_Dec;
1485 NextChr();
1486 }
1487 else if (Chr == '>')
1488 {
1489 Token = TK_Arrow;
1490 NextChr();
1491 }
1492 else
1493 {
1494 Token = TK_Minus;
1495 }
1496 break;
1497
1498 case '*':
1499 if (Chr == '=')
1500 {
1501 Token = TK_MultiplyAssign;
1502 NextChr();
1503 }
1504 else
1505 {
1506 Token = TK_Asterisk;
1507 }
1508 break;
1509
1510 case '/':
1511 if (Chr == '=')
1512 {
1513 Token = TK_DivideAssign;
1514 NextChr();
1515 }
1516 else
1517 {
1518 Token = TK_Slash;
1519 }
1520 break;
1521
1522 case '%':
1523 if (Chr == '=')
1524 {
1525 Token = TK_ModAssign;
1526 NextChr();
1527 }
1528 else
1529 {
1530 Token = TK_Percent;
1531 }
1532 break;
1533
1534 case '=':
1535 if (Chr == '=')
1536 {
1537 Token = TK_Equals;
1538 NextChr();
1539 }
1540 else
1541 {
1542 Token = TK_Assign;
1543 }
1544 break;
1545
1546 case '<':
1547 if (Chr == '<')
1548 {
1549 NextChr();
1550 if (Chr == '=')
1551 {
1552 Token = TK_LShiftAssign;
1553 NextChr();
1554 }
1555 else
1556 {
1557 Token = TK_LShift;
1558 }
1559 }
1560 else if (Chr == '=')
1561 {
1562 Token = TK_LessEquals;
1563 NextChr();
1564 }
1565 else
1566 {
1567 Token = TK_Less;
1568 }
1569 break;
1570
1571 case '>':
1572 if (Chr == '>')
1573 {
1574 NextChr();
1575 if (Chr == '=')
1576 {
1577 Token = TK_RShiftAssign;
1578 NextChr();
1579 }
1580 else
1581 {
1582 Token = TK_RShift;
1583 }
1584 }
1585 else if (Chr == '=')
1586 {
1587 Token = TK_GreaterEquals;
1588 NextChr();
1589 }
1590 else
1591 {
1592 Token = TK_Greater;
1593 }
1594 break;
1595
1596 case '!':
1597 if (Chr == '=')
1598 {
1599 Token = TK_NotEquals;
1600 NextChr();
1601 }
1602 else
1603 {
1604 Token = TK_Not;
1605 }
1606 break;
1607
1608 case '&':
1609 if (Chr == '=')
1610 {
1611 Token = TK_AndAssign;
1612 NextChr();
1613 }
1614 else if (Chr == '&')
1615 {
1616 Token = TK_AndLog;
1617 NextChr();
1618 }
1619 else
1620 {
1621 Token = TK_And;
1622 }
1623 break;
1624
1625 case '|':
1626 if (Chr == '=')
1627 {
1628 Token = TK_OrAssign;
1629 NextChr();
1630 }
1631 else if (Chr == '|')
1632 {
1633 Token = TK_OrLog;
1634 NextChr();
1635 }
1636 else
1637 {
1638 Token = TK_Or;
1639 }
1640 break;
1641
1642 case '^':
1643 if (Chr == '=')
1644 {
1645 Token = TK_XOrAssign;
1646 NextChr();
1647 }
1648 else
1649 {
1650 Token = TK_XOr;
1651 }
1652 break;
1653
1654 case '.':
1655 if (Chr == '.' && Src->FilePtr[0] == '.')
1656 {
1657 Token = TK_VarArgs;
1658 NextChr();
1659 NextChr();
1660 }
1661 else
1662 {
1663 Token = TK_Dot;
1664 }
1665 break;
1666
1667 case ':':
1668 if (Chr == ':')
1669 {
1670 Token = TK_DColon;
1671 NextChr();
1672 }
1673 else
1674 {
1675 Token = TK_Colon;
1676 }
1677 break;
1678
1679 case '(':
1680 Token = TK_LParen;
1681 break;
1682
1683 case ')':
1684 Token = TK_RParen;
1685 break;
1686
1687 case '?':
1688 Token = TK_Quest;
1689 break;
1690
1691 case '~':
1692 Token = TK_Tilde;
1693 break;
1694
1695 case ',':
1696 Token = TK_Comma;
1697 break;
1698
1699 case ';':
1700 Token = TK_Semicolon;
1701 break;
1702
1703 case '[':
1704 Token = TK_LBracket;
1705 break;
1706
1707 case ']':
1708 Token = TK_RBracket;
1709 break;
1710
1711 case '{':
1712 Token = TK_LBrace;
1713 break;
1714
1715 case '}':
1716 Token = TK_RBrace;
1717 break;
1718
1719 default:
1720 ParseError(Location, ERR_BAD_CHARACTER, "Unknown punctuation \'%c\'", Chr);
1721 Token = TK_NoToken;
1722 }
1723 }
1724
1725 //==========================================================================
1726 //
1727 // VLexer::ProcessFileName
1728 //
1729 //==========================================================================
1730
ProcessFileName()1731 void VLexer::ProcessFileName()
1732 {
1733 int len = 0;
1734 NextChr();
1735 while (Chr != '\"')
1736 {
1737 if (len >= MAX_QUOTED_LENGTH - 1)
1738 {
1739 ParseError(Location, ERR_STRING_TOO_LONG);
1740 NextChr();
1741 continue;
1742 }
1743 if (Chr == EOF_CHARACTER)
1744 {
1745 ParseError(Location, ERR_EOF_IN_STRING);
1746 break;
1747 }
1748 if (Src->IncLineNumber)
1749 {
1750 ParseError(Location, ERR_NEW_LINE_INSIDE_QUOTE);
1751 }
1752 TokenStringBuffer[len] = Chr;
1753 NextChr();
1754 len++;
1755 }
1756 TokenStringBuffer[len] = 0;
1757 NextChr();
1758 }
1759
1760 //==========================================================================
1761 //
1762 // VLexer::Check
1763 //
1764 //==========================================================================
1765
Check(EToken tk)1766 bool VLexer::Check(EToken tk)
1767 {
1768 if (Token == tk)
1769 {
1770 NextToken();
1771 return true;
1772 }
1773 return false;
1774 }
1775
1776 //==========================================================================
1777 //
1778 // VLexer::Expect
1779 //
1780 // Report error, if current token is not equals to tk.
1781 // Take next token.
1782 //
1783 //==========================================================================
1784
Expect(EToken tk)1785 void VLexer::Expect(EToken tk)
1786 {
1787 if (Token != tk)
1788 {
1789 ParseError(Location, "expected %s, found %s", TokenNames[tk],
1790 TokenNames[Token]);
1791 }
1792 NextToken();
1793 }
1794
1795 //==========================================================================
1796 //
1797 // VLexer::Expect
1798 //
1799 // Report error, if current token is not equals to tk.
1800 // Take next token.
1801 //
1802 //==========================================================================
1803
Expect(EToken tk,ECompileError error)1804 void VLexer::Expect(EToken tk, ECompileError error)
1805 {
1806 if (Token != tk)
1807 {
1808 ParseError(Location, error, "expected %s, found %s", TokenNames[tk],
1809 TokenNames[Token]);
1810 }
1811 NextToken();
1812 }
1813