1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
25 //**************************************************************************
26 #if !defined(VCC_STANDALONE_EXECUTOR)
27 # include "gamedefs.h"
28 # include "language.h"
29 # include "filesys/files.h"
30 #elif defined(VCC_STANDALONE_EXECUTOR)
31 # include "../../vccrun/vcc_run_vc.h"
32 # include "../../libs/vavoomc/vc_public.h"
36 #if !defined(VCC_STANDALONE_EXECUTOR)
37 static VCvarB
dbg_show_name_remap("dbg_show_name_remap", false, "Show hacky name remapping", CVAR_PreInit
|CVAR_NoShadow
);
41 // ////////////////////////////////////////////////////////////////////////// //
42 class VScriptsParser
: public VObject
{
43 DECLARE_CLASS(VScriptsParser
, VObject
, 0)
44 NO_DEFAULT_CONSTRUCTOR(VScriptsParser
)
48 virtual void Destroy () override
;
49 void CheckInterface ();
51 #if !defined(VCC_STANDALONE_EXECUTOR)
52 DECLARE_FUNCTION(OpenLumpName
)
53 DECLARE_FUNCTION(OpenLumpIndex
)
55 DECLARE_FUNCTION(OpenLumpFullName
)
56 DECLARE_FUNCTION(OpenString
)
57 DECLARE_FUNCTION(get_String
)
58 DECLARE_FUNCTION(get_Number
)
59 DECLARE_FUNCTION(get_Float
)
60 DECLARE_FUNCTION(get_Crossed
)
61 DECLARE_FUNCTION(get_Quoted
)
62 DECLARE_FUNCTION(get_SourceLump
)
63 DECLARE_FUNCTION(set_SourceLump
)
64 DECLARE_FUNCTION(get_Escape
)
65 DECLARE_FUNCTION(set_Escape
)
66 DECLARE_FUNCTION(get_CMode
)
67 DECLARE_FUNCTION(set_CMode
)
68 DECLARE_FUNCTION(get_AllowNumSign
)
69 DECLARE_FUNCTION(set_AllowNumSign
)
70 DECLARE_FUNCTION(get_EDGEMode
)
71 DECLARE_FUNCTION(set_EDGEMode
)
72 DECLARE_FUNCTION(get_EndOfText
)
73 DECLARE_FUNCTION(get_SemicolonComments
)
74 DECLARE_FUNCTION(set_SemicolonComments
)
75 DECLARE_FUNCTION(get_HashComments
)
76 DECLARE_FUNCTION(set_HashComments
)
77 DECLARE_FUNCTION(IsText
)
78 DECLARE_FUNCTION(IsAtEol
)
79 DECLARE_FUNCTION(IsCMode
)
80 DECLARE_FUNCTION(IsAllowNumSign
)
81 DECLARE_FUNCTION(SetCMode
)
82 DECLARE_FUNCTION(SetAllowNumSign
)
83 DECLARE_FUNCTION(IsEscape
)
84 DECLARE_FUNCTION(SetEscape
)
85 DECLARE_FUNCTION(AtEnd
)
86 DECLARE_FUNCTION(GetString
)
87 #if !defined(VCC_STANDALONE_EXECUTOR)
88 DECLARE_FUNCTION(ExpectColor
)
90 DECLARE_FUNCTION(ExpectString
)
91 DECLARE_FUNCTION(ExpectLoneChar
)
92 DECLARE_FUNCTION(Check
)
93 DECLARE_FUNCTION(CheckStartsWith
)
94 DECLARE_FUNCTION(Expect
)
95 DECLARE_FUNCTION(CheckIdentifier
)
96 DECLARE_FUNCTION(ExpectIdentifier
)
97 DECLARE_FUNCTION(CheckNumber
)
98 DECLARE_FUNCTION(ExpectNumber
)
99 DECLARE_FUNCTION(CheckFloat
)
100 DECLARE_FUNCTION(ExpectFloat
)
101 DECLARE_FUNCTION(ResetQuoted
)
102 DECLARE_FUNCTION(ResetCrossed
)
103 DECLARE_FUNCTION(SkipBracketed
)
104 DECLARE_FUNCTION(UnGet
)
105 DECLARE_FUNCTION(SkipLine
)
106 DECLARE_FUNCTION(FileName
)
107 DECLARE_FUNCTION(CurrLine
)
108 DECLARE_FUNCTION(TokenLine
)
109 DECLARE_FUNCTION(ScriptError
)
110 DECLARE_FUNCTION(ScriptMessage
)
112 DECLARE_FUNCTION(SavePos
)
113 DECLARE_FUNCTION(RestorePos
)
115 #if !defined(VCC_STANDALONE_EXECUTOR)
116 DECLARE_FUNCTION(FindRelativeIncludeLump
)
117 DECLARE_FUNCTION(FindIncludeLump
)
121 IMPLEMENT_CLASS(V
, ScriptsParser
)
124 static uint32_t c2spec
[256/32]; // c-like two-chars specials
125 static uint32_t cidterm
[256/32]; // c-like identifier terminator
126 static uint32_t cnumterm
[256/32]; // c-like number terminator
127 static uint32_t ncidterm
[256/32]; // non-c-like identifier terminator
129 struct CharClassifier
{
130 static VVA_FORCEINLINE
bool isC2Spec (char ch
) noexcept
{ return (c2spec
[(ch
>>5)&0x07]&(1U<<(ch
&0x1f))); }
131 static VVA_FORCEINLINE
bool isCIdTerm (char ch
) noexcept
{ return (cidterm
[(ch
>>5)&0x07]&(1U<<(ch
&0x1f))); }
132 static VVA_FORCEINLINE
bool isCNumTerm (char ch
) noexcept
{ return (cnumterm
[(ch
>>5)&0x07]&(1U<<(ch
&0x1f))); }
133 static VVA_FORCEINLINE
bool isNCIdTerm (char ch
, bool hashCmt
) noexcept
{ return (ncidterm
[(ch
>>5)&0x07]&(1U<<(ch
&0x1f))) || (hashCmt
&& ch
== '#'); }
135 static VVA_FORCEINLINE
void setCharBit (vuint32
*set
, char ch
) noexcept
{ set
[(ch
>>5)&0x07] |= (1U<<(ch
&0x1f)); }
137 static VVA_FORCEINLINE
bool isDigit (char ch
) noexcept
{ return (ch
>= '0' && ch
<= '9'); }
139 static inline bool isNumStart (const char *s
, bool allowNumSign
) noexcept
{
140 if (allowNumSign
) if (*s
== '+' || *s
== '-') ++s
;
145 CharClassifier () noexcept
{
146 /*static*/ const char *cIdTerm
= "`~!#$%^&*(){}[]/=\\?-+|;:<>,\"'"; // was with '@'
147 /*static*/ const char *ncIdTerm
= "{}|=,;\"'";
148 /*static*/ const char *c2specStr
= "=!<>+-*/%&|^~";
149 memset(c2spec
, 0, sizeof(cidterm
));
150 memset(cidterm
, 0, sizeof(cidterm
));
151 memset(cnumterm
, 0, sizeof(cnumterm
));
152 memset(ncidterm
, 0, sizeof(ncidterm
));
153 for (const char *s
= c2specStr
; *s
; ++s
) {
154 setCharBit(c2spec
, *s
);
156 for (const char *s
= cIdTerm
; *s
; ++s
) {
157 setCharBit(cidterm
, *s
);
158 setCharBit(cnumterm
, *s
);
160 for (const char *s
= ncIdTerm
; *s
; ++s
) setCharBit(ncidterm
, *s
);
161 // blanks will terminate too
162 for (int ch
= 0; ch
<= 32; ++ch
) {
163 setCharBit(cidterm
, ch
);
164 setCharBit(cnumterm
, ch
);
165 setCharBit(ncidterm
, ch
);
167 // c identified is terminated with dot
168 setCharBit(cidterm
, '.');
170 for (int f
= 0; f
< 256; ++f
) {
171 if (f
<= 32 || f
== '.' || strchr(cIdTerm
, f
)) {
172 vassert(isCIdTerm(f
));
173 if (f
== '.') { vassert(!isCNumTerm(f
)); } else { vassert(isCNumTerm(f
)); }
175 vassert(!isCIdTerm(f
));
176 vassert(!isCNumTerm(f
));
182 CharClassifier charClassifierInit
;
186 //==========================================================================
188 // VScriptSavedPos::saveFrom
190 //==========================================================================
191 void VScriptSavedPos::saveFrom (const VScriptParser
&par
) noexcept
{
193 TokLine
= par
.TokLine
;
200 ScriptPtr
= par
.ScriptPtr
;
201 TokStartPtr
= par
.TokStartPtr
;
202 TokStartLine
= par
.TokStartLine
;
204 (par
.CMode
? Flag_CMode
: Flag_None
)|
205 (par
.Escape
? Flag_Escape
: Flag_None
)|
206 (par
.AllowNumSign
? Flag_AllowNumSign
: Flag_None
)|
207 (par
.EDGEMode
? Flag_EDGEMode
: Flag_None
)|
208 (par
.SemiComments
? Flag_SemiComments
: Flag_None
)|
209 (par
.HashComments
? Flag_HashComments
: Flag_None
)|
211 (par
.End
? Flag_End
: Flag_None
)|
212 (par
.Crossed
? Flag_Crossed
: Flag_None
)|
213 (par
.QuotedString
? Flag_QuotedString
: Flag_None
)|
218 //==========================================================================
220 // VScriptSavedPos::restoreTo
222 //==========================================================================
223 void VScriptSavedPos::restoreTo (VScriptParser
&par
) const noexcept
{
225 par
.TokLine
= TokLine
;
232 par
.ScriptPtr
= ScriptPtr
;
233 par
.TokStartPtr
= TokStartPtr
;
234 par
.TokStartLine
= TokStartLine
;
236 par
.CMode
= !!(flags
&Flag_CMode
);
237 par
.Escape
= !!(flags
&Flag_Escape
);
238 par
.AllowNumSign
= !!(flags
&Flag_AllowNumSign
);
239 par
.EDGEMode
= !!(flags
&Flag_EDGEMode
);
240 par
.SemiComments
= !!(flags
&Flag_SemiComments
);
241 par
.HashComments
= !!(flags
&Flag_HashComments
);
242 par
.End
= !!(flags
&Flag_End
);
243 par
.Crossed
= !!(flags
&Flag_Crossed
);
244 par
.QuotedString
= !!(flags
&Flag_QuotedString
);
249 //==========================================================================
251 // VScriptParser::VScriptParser
253 //==========================================================================
254 VScriptParser::VScriptParser (VStr name
, VStream
*Strm
, int aSourceLump
)
259 , QuotedString(false)
260 , ScriptName(name
.cloneUniqueMT())
264 , AllowNumSign(false)
266 , SemiComments(false)
267 , HashComments(false)
268 , SourceLump(aSourceLump
)
272 ScriptBuffer
= new char[ScriptSize
+1];
273 ScriptBuffer
[0] = '\n';
277 if (Strm
->IsError()) Host_Error("cannot read definition file '%s:%s'", *name
, *Strm
->GetName());
278 ScriptSize
= Strm
->TotalSize();
279 if (Strm
->IsError()) Host_Error("cannot read definition file '%s:%s'", *name
, *Strm
->GetName());
280 ScriptBuffer
= new char[ScriptSize
+1];
281 Strm
->Serialise(ScriptBuffer
, ScriptSize
);
282 ScriptBuffer
[ScriptSize
] = 0;
283 if (Strm
->IsError()) { delete ScriptBuffer
; Host_Error("cannot read definition file '%s:%s'", *name
, *Strm
->GetName()); }
285 VStream::Destroy(Strm
);
288 VStream::Destroy(Strm
);
291 ScriptPtr
= ScriptBuffer
;
292 ScriptEndPtr
= ScriptPtr
+ScriptSize
;
294 TokStartPtr
= ScriptPtr
;
297 // skip garbage some editors add in the begining of UTF-8 files
298 if (*(const vuint8
*)ScriptPtr
== 0xef && *(const vuint8
*)(ScriptPtr
+1) == 0xbb && *(const vuint8
*)(ScriptPtr
+2) == 0xbf) ScriptPtr
+= 3;
302 //==========================================================================
304 // VScriptParser::VScriptParser
306 //==========================================================================
307 VScriptParser::VScriptParser (VStr name
, const char *atext
, int aSourceLump
) noexcept
312 , QuotedString(false)
313 , ScriptName(name
.cloneUniqueMT())
317 , AllowNumSign(false)
319 , SemiComments(false)
320 , HashComments(false)
321 , SourceLump(aSourceLump
)
323 if (atext
&& atext
[0]) {
324 ScriptSize
= (int)strlen(atext
);
325 ScriptBuffer
= new char[ScriptSize
+1];
326 if (ScriptSize
) memcpy(ScriptBuffer
, atext
, ScriptSize
);
327 ScriptBuffer
[ScriptSize
] = 0;
330 ScriptBuffer
= new char[ScriptSize
+1];
334 ScriptPtr
= ScriptBuffer
;
335 ScriptEndPtr
= ScriptPtr
+ScriptSize
;
337 TokStartPtr
= ScriptPtr
;
340 // skip garbage some editors add in the begining of UTF-8 files
341 if (*(const vuint8
*)ScriptPtr
== 0xef && *(const vuint8
*)(ScriptPtr
+1) == 0xbb && *(const vuint8
*)(ScriptPtr
+2) == 0xbf) ScriptPtr
+= 3;
345 #if !defined(VCC_STANDALONE_EXECUTOR)
346 //==========================================================================
348 // VScriptParser::NewWithLump
350 //==========================================================================
351 VScriptParser
*VScriptParser::NewWithLump (int Lump
) {
352 if (Lump
< 0) return new VScriptParser("{nullfile}", "");
353 VStream
*st
= W_CreateLumpReaderNum(Lump
);
354 if (!st
) return new VScriptParser("{nullfile}", "");
355 return new VScriptParser(W_FullLumpName(Lump
), st
, Lump
);
360 //==========================================================================
362 // VScriptParser::~VScriptParser
364 //==========================================================================
365 VScriptParser::~VScriptParser () noexcept
{
366 delete[] ScriptBuffer
;
367 ScriptBuffer
= nullptr;
371 //==========================================================================
373 // VScriptParser::clone
375 //==========================================================================
376 VScriptParser
*VScriptParser::clone () const noexcept
{
377 VScriptParser
*res
= new VScriptParser();
379 res
->ScriptBuffer
= new char[ScriptSize
+1];
380 if (ScriptSize
) memcpy(res
->ScriptBuffer
, ScriptBuffer
, ScriptSize
);
381 res
->ScriptBuffer
[ScriptSize
] = 0;
383 res
->ScriptPtr
= res
->ScriptBuffer
+(ScriptPtr
-ScriptBuffer
);
384 res
->ScriptEndPtr
= res
->ScriptBuffer
+(ScriptEndPtr
-ScriptBuffer
);
386 res
->TokStartPtr
= res
->ScriptBuffer
+(TokStartPtr
-ScriptBuffer
);
387 res
->TokStartLine
= res
->TokStartLine
;
390 res
->TokLine
= TokLine
;
392 res
->Crossed
= Crossed
;
393 res
->QuotedString
= QuotedString
;
394 res
->String
= String
;
397 res
->Number
= Number
;
400 res
->ScriptName
= ScriptName
.cloneUniqueMT();
401 res
->ScriptSize
= ScriptSize
;
403 res
->Escape
= Escape
;
404 res
->AllowNumSign
= AllowNumSign
;
405 res
->EDGEMode
= EDGEMode
;
406 res
->SemiComments
= SemiComments
;
407 res
->HashComments
= HashComments
;
408 res
->SourceLump
= SourceLump
;
414 //==========================================================================
416 // VScriptParser::IsText
418 //==========================================================================
419 bool VScriptParser::IsText () noexcept
{
421 while (i
< ScriptSize
) {
422 vuint8 ch
= *(const vuint8
*)(ScriptBuffer
+(i
++));
423 if (ch
== 127) return false;
424 if (ch
< ' ' && ch
!= '\n' && ch
!= '\r' && ch
!= '\t') return false;
425 if (ch
< 128) continue;
428 if ((ch
&0xe0) == 0xc0) { val
= ch
&0x1f; cnt
= 1; }
429 else if ((ch
&0xf0) == 0xe0) { val
= ch
&0x0f; cnt
= 2; }
430 else if ((ch
&0xf8) == 0xf0) { val
= ch
&0x07; cnt
= 3; }
431 else return false; // invalid utf8
433 if (i
>= ScriptSize
) return false;
434 ch
= ScriptBuffer
[i
++];
435 if ((ch
&0xc0) != 0x80) return false; // invalid utf8
436 val
= (val
<<6)|(ch
&0x3f);
438 // check for valid codepoint
439 if (!(val
< 0xD800 || (val
> 0xDFFF && val
<= 0x10FFFF))) return false; // invalid codepoint
445 //==========================================================================
447 // VScriptParser::AtEnd
449 //==========================================================================
450 bool VScriptParser::AtEnd () noexcept
{
452 //fprintf(stderr, "<%s>\n", *String);
460 //==========================================================================
462 // VScriptParser::IsAtEol
464 //==========================================================================
465 bool VScriptParser::IsAtEol () noexcept
{
466 int commentLevel
= 0;
467 for (const char *s
= ScriptPtr
; s
< ScriptEndPtr
; ++s
) {
468 const vuint8 ch
= *(const vuint8
*)s
;
469 if (ch
== '\r' && s
[1] == '\n') return true;
470 if (ch
== '\n') return true;
472 if ((!CMode
|| SemiComments
) && ch
== ';') return true; // this is single-line comment, it always ends with EOL
473 if (HashComments
&& ch
== '#') return true; // this is single-line comment, it always ends with EOL
474 const char c1
= s
[1];
475 if (ch
== '/' && c1
== '/') return true; // this is single-line comment, it always ends with EOL
476 if (ch
== '/' && c1
== '*') {
482 if (ch
== '/' && c1
== '+') {
488 if (ch
> ' ') return false;
490 // in multiline comment
491 const char c1
= s
[1];
492 if (commentLevel
< 0) {
493 if (ch
== '*' && c1
== '/') {
498 if (ch
== '/' && c1
== '+') {
501 } else if (ch
== '+' && c1
== '/') {
512 //==========================================================================
514 // VScriptParser::SkipComments
516 // this is moved out of `SkipBlanks()`, so i can use it in `SkipLine()`
518 //==========================================================================
519 void VScriptParser::SkipComments (bool changeFlags
) noexcept
{
520 while (ScriptPtr
< ScriptEndPtr
) {
521 const char c0
= *ScriptPtr
;
522 const char c1
= (ScriptPtr
+1 < ScriptEndPtr
? ScriptPtr
[1] : 0);
523 // single-line comment?
524 if (((!CMode
|| SemiComments
) && c0
== ';') || (c0
== '/' && c1
== '/') ||
525 (HashComments
&& c0
== '#'))
527 while (*ScriptPtr
++ != '\n') if (ScriptPtr
>= ScriptEndPtr
) break;
528 if (changeFlags
) { ++Line
; Crossed
= true; }
531 // multiline comment?
532 if (c0
== '/' && c1
== '*') {
534 while (ScriptPtr
< ScriptEndPtr
) {
535 if (ScriptPtr
[0] == '*' && ScriptPtr
[1] == '/') { ScriptPtr
+= 2; break; }
536 // check for new-line character
537 if (changeFlags
&& *ScriptPtr
== '\n') { ++Line
; Crossed
= true; }
542 // multiline nesting comment?
543 if (c0
== '/' && c1
== '+') {
546 while (ScriptPtr
< ScriptEndPtr
) {
547 if (ScriptPtr
[0] == '/' && ScriptPtr
[1] == '+') { ScriptPtr
+= 2; ++level
; continue; }
548 if (ScriptPtr
[0] == '+' && ScriptPtr
[1] == '/') {
550 if (--level
== 0) break;
553 // check for new-line character
554 if (changeFlags
&& *ScriptPtr
== '\n') { ++Line
; Crossed
= true; }
559 // not a comment, stop skipping
562 if (ScriptPtr
>= ScriptEndPtr
) {
563 ScriptPtr
= ScriptEndPtr
;
564 if (changeFlags
) End
= true;
569 //==========================================================================
571 // VScriptParser::SkipBlanks
573 //==========================================================================
574 void VScriptParser::SkipBlanks (bool changeFlags
) noexcept
{
575 while (ScriptPtr
< ScriptEndPtr
) {
576 SkipComments(changeFlags
);
577 if (*(const vuint8
*)ScriptPtr
<= ' ') {
578 if (changeFlags
&& *ScriptPtr
== '\n') { ++Line
; Crossed
= true; }
584 if (ScriptPtr
>= ScriptEndPtr
) {
585 ScriptPtr
= ScriptEndPtr
;
586 if (changeFlags
) End
= true;
591 //==========================================================================
593 // VScriptParser::PeekChar
595 //==========================================================================
596 char VScriptParser::PeekOrSkipChar (bool doSkip
) noexcept
{
598 char *oldSPtr
= ScriptPtr
;
600 bool oldCross
= Crossed
;
602 SkipBlanks(true); // change flags
603 if (ScriptPtr
< ScriptEndPtr
) {
605 if (doSkip
) ++ScriptPtr
;
617 //==========================================================================
619 // VScriptParser::ParseQuotedString
621 // starting quite is eaten
623 //==========================================================================
624 void VScriptParser::ParseQuotedString (const char qch
) noexcept
{
625 while (ScriptPtr
< ScriptEndPtr
) {
626 char ch
= *ScriptPtr
++;
630 // double quote is string continuation in C mode
631 if (*ScriptPtr
!= qch
) break;
633 if ((vuint8
)ScriptPtr
[1] < ' ') break;
634 ++ScriptPtr
; // skip quote char
638 if (ch
== '\n' || ch
== '\r') {
639 // convert from DOS format to UNIX format
640 if (ch
== '\r' && *ScriptPtr
== '\n') ++ScriptPtr
;
643 /*if (String.length() == 0)*/ Error("Unterminated string constant");
652 if (Escape
&& ch
== '\\' && ScriptPtr
[0]) {
653 const char c1
= *ScriptPtr
++;
655 if (c1
== '\n' || c1
== '\r') {
658 if (c1
== '\r' && *ScriptPtr
== '\n') ++ScriptPtr
;
663 bool okescape
= true;
665 case 'r': ch
= '\r'; break;
666 case 'n': ch
= '\n'; break;
667 case 'c': case 'C': ch
= TEXT_COLOR_ESCAPE
; break;
668 case 'e': ch
= '\x1b'; break;
669 case '\t': ch
= '\t'; break;
670 case '"': case '\'': case ' ': case '\\': case '`': ch
= c1
; break;
672 if (VStr::digitInBase(*ScriptPtr
, 16) < 0) {
675 int n0
= VStr::digitInBase(*ScriptPtr
++, 16);
676 int n1
= VStr::digitInBase(*ScriptPtr
, 16);
677 if (n1
>= 0) { n0
= n0
*16+n1
; ++ScriptPtr
; }
682 default: okescape
= false; break;
685 if (!okescape
) String
+= c1
;
688 bool okescape
= true;
690 case 'r': ch
= '\r'; break;
691 case 'n': ch
= '\n'; break;
692 case 'c': case 'C': ch
= TEXT_COLOR_ESCAPE
; break;
693 case '\\': case '\"': case '\'': case '`': ch
= c1
; break;
694 default: okescape
= false; break;
700 // escaped eol and eol == one eol
701 if (ch
== '\r' || ch
== '\n') {
702 if (*ScriptPtr
== '\n') {
706 } else if (*ScriptPtr
== '\r' && ScriptPtr
[1] == '\n') {
722 //==========================================================================
726 //==========================================================================
727 static const char *skipNum (const char *s
, const char *end
, int base
) {
728 if (base
<= 0) base
= 10;
729 if (s
>= end
) return nullptr;
730 if (VStr::digitInBase(*s
, base
) < 0) return nullptr;
734 if (VStr::digitInBase(*s
, base
) < 0) return s
;
742 //==========================================================================
746 // returns number end, or `nullptr` if definitely not a number
748 //==========================================================================
749 static const char *isValidNum (const char *s
, const char *end
) {
750 if (s
>= end
) return nullptr;
753 if (*s
== '+' || *s
== '-') {
754 if (++s
>= end
) return nullptr;
758 if (*s
== '0' && s
+1 < end
&& s
+2 < end
&& (s
[1] == 'x' || s
[1] == 'X')) {
760 return skipNum(s
, end
, 16);
765 s
= skipNum(s
, end
, 10);
766 if (!s
|| s
>= end
) return s
;
768 // no integral part, so fractional part should have at least one digit
769 if (s
+1 >= end
) return nullptr;
770 if (s
[1] < '0' || s
[1] > '9') return nullptr;
775 if (++s
>= end
) return s
;
776 if (*s
>= '0' && *s
<= '9') {
777 s
= skipNum(s
, end
, 10);
778 if (!s
|| s
>= end
) return s
;
783 if (*s
!= 'e' && *s
!= 'E') return s
;
784 if (++s
>= end
) return nullptr;
787 if (*s
== '+' || *s
== '-') {
788 if (++s
>= end
) return nullptr;
792 return skipNum(s
, end
, 10);
796 //==========================================================================
798 // VScriptParser::ParseCMode
800 //==========================================================================
801 void VScriptParser::ParseCMode () noexcept
{
802 if (ScriptPtr
+1 < ScriptEndPtr
) {
803 // special double-character eq-token?
804 if (ScriptPtr
[1] == '=' && CharClassifier::isC2Spec(ScriptPtr
[0])) {
805 String
+= *ScriptPtr
++;
806 String
+= *ScriptPtr
++;
810 // special double-character token?
811 if ((ScriptPtr
[0] == '&' && ScriptPtr
[1] == '&') ||
812 (ScriptPtr
[0] == '|' && ScriptPtr
[1] == '|') ||
813 (ScriptPtr
[0] == '<' && ScriptPtr
[1] == '<') ||
814 (ScriptPtr
[0] == '>' && ScriptPtr
[1] == '>') ||
815 (ScriptPtr
[0] == ':' && ScriptPtr
[1] == ':') ||
816 (ScriptPtr
[0] == '+' && ScriptPtr
[1] == '+') ||
817 (ScriptPtr
[0] == '-' && ScriptPtr
[1] == '-'))
819 if (ScriptPtr
[0] == '>' && ScriptPtr
[1] == '>' && ScriptPtr
[2] == '>') String
+= *ScriptPtr
++; // for `>>>`
820 String
+= *ScriptPtr
++;
821 String
+= *ScriptPtr
++;
827 // have to do it this way to omit underscores
828 if (CharClassifier::isNumStart(ScriptPtr
, AllowNumSign
)) {
829 const char *ee
= isValidNum(ScriptPtr
, ScriptEndPtr
);
830 if (ee
&& (ee
>= ScriptEndPtr
|| CharClassifier::isCIdTerm(*ee
))) {
831 // looks like a valid number
832 while (ScriptPtr
< ee
) {
833 const char ch
= *ScriptPtr
++;
834 if (ch
== '_') continue;
841 // special single-character token?
842 if (CharClassifier::isCIdTerm(*ScriptPtr
)) {
843 String
+= *ScriptPtr
++;
848 while (ScriptPtr
< ScriptEndPtr
) {
849 const char ch
= *ScriptPtr
++;
850 // eh... allow single quote inside an identifier
851 if (ch
== '\'' && !String
.isEmpty() && ScriptPtr
[0] && !CharClassifier::isCIdTerm(ScriptPtr
[0])) {
855 if (CharClassifier::isCIdTerm(ch
)) { --ScriptPtr
; break; }
856 if (ch
!= '_' || !EDGEMode
) String
+= ch
;
861 //==========================================================================
863 // VScriptParser::ParseNonCMode
865 //==========================================================================
866 void VScriptParser::ParseNonCMode () noexcept
{
867 // special single-character tokens
868 if (CharClassifier::isNCIdTerm(*ScriptPtr
, HashComments
)) {
869 String
+= *ScriptPtr
++;
874 while (ScriptPtr
< ScriptEndPtr
) {
875 const char ch
= *ScriptPtr
++;
876 // eh... allow single quote inside an identifier
877 if (ch
== '\'' && !String
.isEmpty() && ScriptPtr
[0] && !CharClassifier::isNCIdTerm(ScriptPtr
[0], HashComments
)) {
881 if (CharClassifier::isNCIdTerm(ch
, HashComments
)) { --ScriptPtr
; break; }
882 // check for comments
884 const char c1
= *ScriptPtr
;
885 if (c1
== '/' || c1
== '*' || c1
== '+') {
890 if (ch
!= '_' || !EDGEMode
) String
+= ch
;
895 //==========================================================================
897 // VScriptParser::GetString
899 //==========================================================================
900 bool VScriptParser::GetString () noexcept
{
901 TokStartPtr
= ScriptPtr
;
905 QuotedString
= false;
907 SkipBlanks(true); // change flags
909 // check for end of script
910 if (ScriptPtr
>= ScriptEndPtr
) {
911 TokStartPtr
= ScriptEndPtr
;
921 if (*ScriptPtr
== '\"' || *ScriptPtr
== '\'') {
923 const char qch
= *ScriptPtr
++;
925 ParseQuotedString(qch
);
936 //==========================================================================
938 // VScriptParser::SkipLine
940 //==========================================================================
941 void VScriptParser::SkipLine () noexcept
{
943 QuotedString
= false;
944 while (ScriptPtr
< ScriptEndPtr
) {
946 if (Crossed
|| ScriptPtr
>= ScriptEndPtr
) break;
947 if (*ScriptPtr
++ == '\n') {
954 if (ScriptPtr
>= ScriptEndPtr
) {
955 ScriptPtr
= ScriptEndPtr
;
959 TokStartPtr
= ScriptEndPtr
;
964 //==========================================================================
966 // VScriptParser::ExpectString
968 //==========================================================================
969 void VScriptParser::ExpectString () {
970 if (!GetString()) Error("String expected");
974 //==========================================================================
976 // VScriptParser::ExpectLoneChar
978 //==========================================================================
979 void VScriptParser::ExpectLoneChar () {
981 char ch
= PeekOrSkipChar(true); // skip
982 if (ch
&& ScriptPtr
< ScriptEndPtr
) {
983 if (ch
== '"' && ScriptPtr
[0] == '\\' && ScriptPtr
[1] && ScriptPtr
[2] == '"') {
986 } else if (ch
== '"' && ScriptPtr
[0] && ScriptPtr
[1] == '"') {
990 // check for delimiter (space or comment)
991 vuint8 nch
= *(const vuint8
*)ScriptPtr
;
992 if (nch
> ' ' && nch
== '/' && ScriptEndPtr
-ScriptPtr
> 1) {
993 if (ScriptPtr
[1] != '/' && ScriptPtr
[1] != '*' && ScriptPtr
[1] != '+') ch
= 0;
997 if (!ch
) Error("Char expected");
1003 //==========================================================================
1005 // ConvertStrToName8
1007 //==========================================================================
1008 static VName
ConvertStrToName8 (VScriptParser
*sc
, VStr str
, bool onlyWarn
=true, VName
*defval
=nullptr) noexcept
{
1009 #if !defined(VCC_STANDALONE_EXECUTOR)
1010 // translate "$name" strings
1011 if (str
.length() > 1 && str
[0] == '$') {
1012 VStr qs
= str
.mid(1, str
.length()-1).toLowerCase();
1013 if (GLanguage
.HasTranslation(*qs
)) {
1014 qs
= *GLanguage
[*qs
];
1015 if (dbg_show_name_remap
) GCon
->Logf(NAME_Debug
, "**** <%s>=<%s>\n", *str
, *qs
);
1021 if (str
.Length() > 8) {
1022 #if !defined(VCC_STANDALONE_EXECUTOR)
1025 if (str
.endsWithCI(".png")) str
.chopRight(4);
1026 else if (str
.endsWithCI(".jpg")) str
.chopRight(4);
1027 else if (str
.endsWithCI(".bmp")) str
.chopRight(4);
1028 else if (str
.endsWithCI(".pcx")) str
.chopRight(4);
1029 else if (str
.endsWithCI(".lmp")) str
.chopRight(4);
1030 else if (str
.endsWithCI(".jpeg")) str
.chopRight(5);
1031 #if !defined(VCC_STANDALONE_EXECUTOR)
1032 if (oldstr
!= str
) {
1033 GCon
->Logf(NAME_Warning
, "%s: Name '%s' converted to '%s'", *sc
->GetLoc().toStringNoCol(), *oldstr
, *str
);
1038 if (str
.Length() > 8) {
1039 #if !defined(VCC_STANDALONE_EXECUTOR)
1040 GCon
->Logf(NAME_Warning
, "%s: Name '%s' is too long", *sc
->GetLoc().toStringNoCol(), *str
);
1042 if (!onlyWarn
) sc
->Error(va("Name '%s' is too long", *str
));
1043 if (defval
) return *defval
;
1046 return VName(*str
, VName::AddLower8
);
1050 //==========================================================================
1052 // VScriptParser::ExpectName8
1054 //==========================================================================
1055 void VScriptParser::ExpectName8 () {
1056 if (!GetString()) Error("Short name expected");
1057 Name8
= ConvertStrToName8(this, String
, false); // error
1061 //==========================================================================
1063 // VScriptParser::ExpectName8Warn
1065 //==========================================================================
1066 void VScriptParser::ExpectName8Warn () {
1067 if (!GetString()) Error("Short name expected");
1068 Name8
= ConvertStrToName8(this, String
); // no error
1072 //==========================================================================
1074 // VScriptParser::ExpectName8WarnOrFilePath
1076 //==========================================================================
1077 bool VScriptParser::ExpectName8WarnOrFilePath () {
1078 if (!GetString()) Error("Short name or path expected");
1079 String
= String
.fixSlashes();
1080 // hack for "vile/1", etc.
1081 const int slpos
= String
.indexOf('/');
1082 if (slpos
< 0 || (String
.length() <= 8 && slpos
>= String
.length()-2)) {
1083 Name8
= ConvertStrToName8(this, String
); // no error
1092 //==========================================================================
1094 // VScriptParser::ExpectName8Def
1096 //==========================================================================
1097 void VScriptParser::ExpectName8Def (VName def
) {
1098 if (!GetString()) Error("Short name expected");
1099 Name8
= ConvertStrToName8(this, String
, true, &def
); // no error
1103 //==========================================================================
1105 // VScriptParser::ExpectName
1107 //==========================================================================
1108 void VScriptParser::ExpectName () {
1109 if (!GetString()) Error("Name expected");
1110 Name
= VName(*String
, VName::AddLower
);
1114 #if !defined(VCC_STANDALONE_EXECUTOR)
1115 //==========================================================================
1119 // returns parsed color, either in string form, or r,g,b triplet
1121 //==========================================================================
1122 vuint32
VScriptParser::ExpectColor () {
1123 if (!GetString()) Error("Color expected");
1124 //vuint32 clr = M_LookupColorName(String);
1125 //if (clr) return clr;
1126 // hack to allow numbers like "008000"
1127 if (QuotedString
|| String
.length() > 3) {
1128 //GCon->Logf("COLOR(0): <%s> (0x%08x)", *String, M_ParseColor(*String));
1129 return M_ParseColor(*String
);
1131 // should be r,g,b triplet
1134 if (!CheckNumber()) {
1136 //GCon->Logf("COLOR(1): <%s> (0x%08x)", *String, M_ParseColor(*String));
1137 return M_ParseColor(*String
);
1139 int r
= clampToByte(Number
);
1142 int g
= clampToByte(Number
);
1145 int b
= clampToByte(Number
);
1146 //GCon->Logf("COLOR: rgb(%d,%d,%d)", r, g, b);
1147 return 0xff000000u
|(r
<<16)|(g
<<8)|b
;
1152 //==========================================================================
1154 // VScriptParser::Check
1156 //==========================================================================
1157 bool VScriptParser::Check (const char *str
) noexcept
{
1161 if (!String
.ICmp(str
)) return true;
1168 //==========================================================================
1170 // VScriptParser::CheckStartsWith
1172 //==========================================================================
1173 bool VScriptParser::CheckStartsWith (const char *str
) noexcept
{
1178 if (String
.length() < s
.length()) { UnGet(); return false; }
1179 VStr s2
= String
.left(s
.length());
1180 if (s2
.ICmp(s
) != 0) { UnGet(); return false; }
1187 //==========================================================================
1189 // VScriptParser::Expect
1191 //==========================================================================
1192 void VScriptParser::Expect (const char *name
) {
1195 if (!GetString()) Error(va("`%s` expected", name
));
1196 if (String
.ICmp(name
)) Error(va("Bad syntax, `%s` expected, got `%s`.", name
, *String
.quote()));
1200 //==========================================================================
1202 // VScriptParser::CheckQuotedString
1204 //==========================================================================
1205 bool VScriptParser::CheckQuotedString () noexcept
{
1206 if (!GetString()) return false;
1207 if (!QuotedString
) {
1215 //==========================================================================
1217 // VScriptParser::CheckIdentifier
1219 //==========================================================================
1220 bool VScriptParser::CheckIdentifier () noexcept
{
1221 if (!GetString()) return false;
1223 // quoted strings are not valid identifiers
1229 if (String
.Length() < 1) {
1234 // identifier must start with a letter, a number or an underscore
1236 if (!((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || (c
>= '0' && c
<= '9') || c
== '_')) {
1241 // it must be followed by letters, numbers and underscores
1242 for (int i
= 1; i
< String
.Length(); ++i
) {
1244 if (!((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || (c
>= '0' && c
<= '9') || c
== '_')) {
1253 //==========================================================================
1255 // VScriptParser::ExpectIdentifier
1257 //==========================================================================
1258 void VScriptParser::ExpectIdentifier () {
1259 if (!CheckIdentifier()) Error(va("Identifier expected, got `%s`.", *String
.quote()));
1263 //==========================================================================
1265 // NormalizeFuckedGozzoNumber
1267 //==========================================================================
1268 static VStr
NormalizeFuckedGozzoNumber (VStr String
) noexcept
{
1269 VStr str
= String
.xstrip();
1270 if (str
.length() && strchr("lfLF", str
[str
.length()-1])) {
1272 str
= String
.xstrip();
1278 //==========================================================================
1280 // VScriptParser::CheckNumber
1282 //==========================================================================
1283 bool VScriptParser::CheckNumber () noexcept
{
1285 VStr str
= NormalizeFuckedGozzoNumber(String
);
1286 if (str
.length() > 0) {
1287 if (str
.convertInt(&Number
)) {
1288 //GCon->Logf("VScriptParser::CheckNumber: <%s> is %d", *String, Number);
1298 //==========================================================================
1300 // VScriptParser::ExpectNumber
1302 //==========================================================================
1303 void VScriptParser::ExpectNumber (bool allowFloat
, bool truncFloat
) {
1305 Error("Integer expected");
1307 VStr str
= NormalizeFuckedGozzoNumber(String
);
1308 if (str
.length() > 0) {
1310 Number
= strtol(*str
, &stopper
, 0);
1311 if (*stopper
!= 0) {
1312 if (allowFloat
&& *stopper
== '.') {
1314 Message(va("Bad numeric constant \"%s\" (integer expected; truncated to %d).", *String
, (int)Number
));
1316 if (stopper
[1] >= '5') ++Number
;
1317 if (Number
== 0) Number
= 1; // just in case
1318 Message(va("Bad numeric constant \"%s\" (integer expected; rounded to %d).", *String
, (int)Number
));
1320 //fprintf(stderr, "%d\n", (int)Number);
1321 //Error(va("Bad numeric constant \"%s\".", *String));
1323 Error(va("Bad numeric constant \"%s\".", *String
));
1327 Error("Integer expected");
1333 //==========================================================================
1335 // VScriptParser::CheckNumberWithSign
1337 //==========================================================================
1338 bool VScriptParser::CheckNumberWithSign () noexcept
{
1339 char *savedPtr
= TokStartPtr
;
1340 int savedLine
= TokStartLine
;
1341 bool neg
= Check("-");
1342 bool pos
= !neg
&& Check("+");
1344 if (CheckNumber()) {
1345 if (neg
) Number
= -Number
;
1349 ScriptPtr
= savedPtr
;
1353 return CheckNumber();
1358 //==========================================================================
1360 // VScriptParser::ExpectNumberWithSign
1362 //==========================================================================
1363 void VScriptParser::ExpectNumberWithSign () {
1373 //==========================================================================
1375 // VScriptParser::parseFloat
1379 //==========================================================================
1380 float VScriptParser::parseFloat (bool silent
, bool *error
) {
1382 if (!error
) error
= &etmp
;
1385 VStr str
= NormalizeFuckedGozzoNumber(String
);
1386 if (str
.length() == 0) {
1388 if (!silent
) Error("Float expected");
1392 //FIXME: detect when we want to use a really big number
1393 VStr sl
= str
.toLowerCase();
1394 if (sl
.StartsWith("0x") || sl
.StartsWith("-0x") || sl
.StartsWith("+0x")) {
1395 const char *s
= *str
;
1405 s
+= 2; // skip "0x"
1410 int dg
= VStr::digitInBase(*s
++, 16);
1413 if (!silent
) Error(va("Bad floating point constant \"%s\"", *String
));
1417 val
= val
*16.0f
+(float)dg
;
1418 if (val
> 1.0e12f
) {
1419 if (!silent
) GLog
.Logf(NAME_Warning
, "%s: DON'T BE IDIOTS, THIS IS TOO MUCH FOR A FLOAT: '%s'", *GetLoc().toStringNoCol(), *String
);
1424 if (!silent
) GLog
.Logf(NAME_Warning
, "%s: hex value '%s' for floating constant", *GetLoc().toStringNoCol(), *String
);
1425 if (neg
) val
= -val
;
1429 if (!str
.convertFloat(&ff
)) {
1430 // mo...dders from LCA loves numbers like "90000000000000000000000000000000000000000000000000"
1431 const char *s
= *str
;
1441 while (*s
>= '0' && *s
<= '9') ++s
;
1444 if (!silent
) Error(va("Bad floating point constant \"%s\".", *String
));
1447 if (!silent
) GLog
.Logf(NAME_Warning
, "%s: DON'T BE IDIOTS, THIS IS TOO MUCH FOR A FLOAT: '%s'", *GetLoc().toStringNoCol(), *String
);
1453 if (!silent
) Error(va("Bad floating point constant \"%s\".", *String
));
1457 if (!silent
) GLog
.Logf(NAME_Warning
, "%s: WUTAFUCK IS THIS SO-CALLED FLOAT!? -- '%s'", *GetLoc().toStringNoCol(), *String
);
1458 ff
= (isPositiveF(ff
) ? 1.0e12f
: -1.0e12f
);
1459 } else if (ff
< -1.0e12f
|| ff
> +1.0e12f
) {
1460 if (!silent
) GLog
.Logf(NAME_Warning
, "%s: DON'T BE IDIOTS, THIS IS TOO MUCH FOR A FLOAT: '%s'", *GetLoc().toStringNoCol(), *String
);
1461 if (ff
< -1.0e12f
) ff
= -1.0e12f
; else ff
= 1.0e12f
;
1469 //==========================================================================
1471 // VScriptParser::CheckFloat
1473 //==========================================================================
1474 bool VScriptParser::CheckFloat () noexcept
{
1475 if (!GetString()) return false;
1476 if (!CMode
|| !AllowNumSign
) {
1477 if (String
.length() && (String
[0] == '+' || String
[0] == '-')) {
1483 const float ff
= parseFloat(true, &error
);
1484 if (error
) UnGet(); else Float
= ff
;
1489 //==========================================================================
1491 // VScriptParser::ExpectFloat
1493 //==========================================================================
1494 void VScriptParser::ExpectFloat () {
1495 if (!GetString()) Error("Float expected");
1497 const float ff
= parseFloat(false, &error
);
1498 if (error
) Error("Float expected");
1503 //==========================================================================
1505 // VScriptParser::CheckFloatWithSign
1507 //==========================================================================
1508 bool VScriptParser::CheckFloatWithSign () noexcept
{
1509 char *savedPtr
= TokStartPtr
;
1510 int savedLine
= TokStartLine
;
1511 bool neg
= Check("-");
1512 bool pos
= !neg
&& Check("+");
1515 if (neg
) Float
= -Float
;
1519 ScriptPtr
= savedPtr
;
1523 return CheckFloat();
1528 //==========================================================================
1530 // VScriptParser::ExpectFloatWithSign
1532 //==========================================================================
1533 void VScriptParser::ExpectFloatWithSign () {
1544 //==========================================================================
1546 // VScriptParser::ResetQuoted
1548 //==========================================================================
1549 void VScriptParser::ResetQuoted () noexcept
{
1550 /*if (TokStartPtr != ScriptPtr)*/ QuotedString
= false;
1554 //==========================================================================
1556 // VScriptParser::ResetCrossed
1558 //==========================================================================
1559 void VScriptParser::ResetCrossed () noexcept
{
1560 /*if (TokStartPtr != ScriptPtr)*/ Crossed
= false;
1564 //==========================================================================
1566 // VScriptParser::UnGet
1568 // Assumes that `GetString()` was called at least once
1570 //==========================================================================
1571 void VScriptParser::UnGet () noexcept
{
1572 //AlreadyGot = true;
1573 ScriptPtr
= TokStartPtr
;
1574 Line
= TokStartLine
;
1579 //==========================================================================
1581 // VScriptParser::SkipBracketed
1583 //==========================================================================
1584 void VScriptParser::SkipBracketed (bool bracketEaten
) noexcept
{
1585 if (!bracketEaten
) {
1588 if (!GetString()) return;
1589 if (QuotedString
) continue;
1590 if (String
.length() == 1 && String
[0] == '{') {
1598 if (!GetString()) break;
1599 if (QuotedString
) continue;
1600 if (String
.length() == 1) {
1601 if (String
[0] == '{') {
1603 } else if (String
[0] == '}') {
1604 if (--level
== 0) return;
1611 //==========================================================================
1613 // VScriptParser::Message
1615 //==========================================================================
1616 void VScriptParser::Message (const char *message
) {
1617 const char *Msg
= (message
? message
: "Bad syntax.");
1618 #if !defined(VCC_STANDALONE_EXECUTOR)
1619 GCon
->Logf(NAME_Warning
, "%s:%d: %s", *ScriptName
, TokLine
, Msg
);
1621 GLog
.WriteLine(NAME_Warning
, "%s:%d: %s", *ScriptName
, TokLine
, Msg
);
1626 //==========================================================================
1628 // VScriptParser::DebugMessage
1630 //==========================================================================
1631 void VScriptParser::DebugMessage (const char *message
) {
1632 const char *Msg
= (message
? message
: "Bad syntax.");
1633 #if !defined(VCC_STANDALONE_EXECUTOR)
1634 GCon
->Logf(NAME_Debug
, "%s:%d: %s", *ScriptName
, TokLine
, Msg
);
1636 GLog
.WriteLine(NAME_Debug
, "%s:%d: %s", *ScriptName
, TokLine
, Msg
);
1641 //==========================================================================
1643 // VScriptParser::MessageErr
1645 //==========================================================================
1646 void VScriptParser::MessageErr (const char *message
) {
1647 const char *Msg
= (message
? message
: "Bad syntax.");
1648 #if !defined(VCC_STANDALONE_EXECUTOR)
1649 GCon
->Logf(NAME_Error
, "%s:%d: %s", *ScriptName
, TokLine
, Msg
);
1651 GLog
.WriteLine(NAME_Error
, "%s:%d: %s", *ScriptName
, TokLine
, Msg
);
1656 //==========================================================================
1658 // VScriptParser::Error
1660 //==========================================================================
1661 void VScriptParser::Error (const char *message
) {
1662 const char *Msg
= (message
? message
: "Bad syntax.");
1663 Sys_Error("Script error at %s:%d: %s", *ScriptName
, TokLine
, Msg
);
1667 //==========================================================================
1669 // VScriptParser::HostError
1671 //==========================================================================
1672 #if !defined(VCC_STANDALONE_EXECUTOR)
1673 void VScriptParser::HostError (const char *message
) {
1674 const char *Msg
= (message
? message
: "Bad syntax.");
1675 Host_Error("Script error at %s:%d: %s", *ScriptName
, TokLine
, Msg
);
1680 //==========================================================================
1682 // VScriptParser::Messagef
1684 //==========================================================================
1685 __attribute__((format(printf
, 2, 3))) void VScriptParser::Messagef (const char *fmt
, ...) {
1688 const char *s
= vavarg(fmt
, ap
);
1694 //==========================================================================
1696 // VScriptParser::DebugMessagef
1698 //==========================================================================
1699 __attribute__((format(printf
, 2, 3))) void VScriptParser::DebugMessagef (const char *fmt
, ...) {
1702 const char *s
= vavarg(fmt
, ap
);
1708 //==========================================================================
1710 // VScriptParser::MessageErrf
1712 //==========================================================================
1713 __attribute__((format(printf
, 2, 3))) void VScriptParser::MessageErrf (const char *fmt
, ...) {
1716 const char *s
= vavarg(fmt
, ap
);
1722 //==========================================================================
1724 // VScriptParser::Errorf
1726 //==========================================================================
1727 __attribute__((format(printf
, 2, 3))) void VScriptParser::Errorf (const char *fmt
, ...) {
1730 const char *s
= vavarg(fmt
, ap
);
1736 #if !defined(VCC_STANDALONE_EXECUTOR)
1737 //==========================================================================
1739 // VScriptParser::HostErrorf
1741 //==========================================================================
1742 __attribute__((format(printf
, 2, 3))) void VScriptParser::HostErrorf (const char *fmt
, ...) {
1745 const char *s
= vavarg(fmt
, ap
);
1752 //==========================================================================
1754 // VScriptParser::GetVCLoc
1756 //==========================================================================
1757 TLocation
VScriptParser::GetVCLoc () const noexcept
{
1758 if (SrcIdx
== -1) SrcIdx
= TLocation::AddSourceFile(ScriptName
);
1759 return TLocation(SrcIdx
, TokLine
, 1);
1763 #if !defined(VCC_STANDALONE_EXECUTOR)
1764 //==========================================================================
1766 // VScriptParser::FindRelativeIncludeLump
1768 //==========================================================================
1769 int VScriptParser::FindRelativeIncludeLump (int srclump
, VStr fname
) noexcept
{
1770 if (fname
.isEmpty()) return -1;
1771 if (srclump
< 0 || fname
[0] == '/') return -1;
1773 VStr fn
= W_RealLumpName(srclump
);
1774 if (fn
.indexOf('/') < 0) return -1;
1775 fn
= fn
.ExtractFilePath();
1776 fname
= fn
.appendPath(fname
);
1778 return W_CheckNumForFileNameInSameFile(srclump
, fname
);
1782 //==========================================================================
1784 // VScriptParser::FindIncludeLump
1786 //==========================================================================
1787 int VScriptParser::FindIncludeLump (int srclump
, VStr fname
) noexcept
{
1788 if (fname
.isEmpty()) return -1;
1789 //GLog.Logf(NAME_Debug, "***FindIncludeLumpEx: srclump=%d; fname='%s' (srcfname='%s')", srclump, *fname, *W_FullLumpName(srclump));
1791 int Lump
= (srclump
>= 0 ? W_CheckNumForFileNameInSameFile(srclump
, fname
) : W_CheckNumForFileName(fname
));
1792 if (Lump
>= 0) return Lump
;
1794 fname
= fname
.fixSlashes();
1795 //GLog.Logf(NAME_Debug, " fname='%s'", *fname);
1797 Lump
= FindRelativeIncludeLump(srclump
, fname
);
1798 if (Lump
>= 0) return Lump
;
1800 //GLog.Logf(NAME_Debug, " XXX: fname='%s'", *fname);
1801 // check WAD lump only if it's no longer than 8 characters and has no path separator
1802 if (/*(srclump == -1 || W_IsWADLump(srclump)) &&*/ fname
.indexOf('/') < 0) {
1803 VStr noext
= fname
.stripExtension();
1804 //GLog.Logf(NAME_Debug, " NOEXT: %s", *noext);
1805 if (!noext
.isEmpty() && noext
.length() <= 8) {
1806 VName
nn(*fname
, VName::FindLower8
);
1807 if (nn
!= NAME_None
) {
1808 //GLog.Logf(NAME_Debug, " NN: %s", *nn);
1809 Lump
= (srclump
< 0 ? W_CheckNumForName(nn
) : W_CheckNumForNameInFile(nn
, W_LumpFile(srclump
)));
1810 if (Lump
>= 0) return Lump
;
1821 // ////////////////////////////////////////////////////////////////////////// //
1823 // ////////////////////////////////////////////////////////////////////////// //
1825 //==========================================================================
1827 // VScriptsParser::Destroy
1829 //==========================================================================
1830 void VScriptsParser::Destroy () {
1839 //==========================================================================
1841 // VScriptsParser::CheckInterface
1843 //==========================================================================
1844 void VScriptsParser::CheckInterface () {
1845 if (!Int
) Sys_Error("No script currently open");
1850 //==========================================================================
1852 // VScriptsParser natives
1854 //==========================================================================
1856 #if !defined(VCC_STANDALONE_EXECUTOR)
1857 IMPLEMENT_FUNCTION(VScriptsParser
, OpenLumpName
) {
1859 vobjGetParamSelf(Name
);
1862 Self
->Int
= nullptr;
1864 int Lump
= W_GetNumForName(Name
);
1865 if (Lump
< 0) Sys_Error("cannot open non-existing lump '%s'", *Name
);
1866 Self
->Int
= VScriptParser::NewWithLump(Lump
);
1869 IMPLEMENT_FUNCTION(VScriptsParser
, OpenLumpIndex
) {
1871 vobjGetParamSelf(lump
);
1874 Self
->Int
= nullptr;
1876 if (lump
< 0) Sys_Error("cannot open non-existing lump");
1877 VStream
*st
= W_CreateLumpReaderNum(lump
);
1878 if (!st
) Sys_Error("cannot open non-existing lump");
1879 Self
->Int
= new VScriptParser(W_FullLumpName(lump
), st
, lump
);
1883 IMPLEMENT_FUNCTION(VScriptsParser
, OpenLumpFullName
) {
1885 vobjGetParamSelf(Name
);
1886 #if !defined(VCC_STANDALONE_EXECUTOR)
1889 Self
->Int
= nullptr;
1891 // first try disk file
1892 if (FL_IsSafeDiskFileName(Name
)) {
1893 VStr diskName
= FL_GetUserDataDir(false)+"/"+Name
;
1894 VStream
*st
= FL_OpenSysFileRead(*diskName
);
1898 if (st
->TotalSize() > 0) {
1899 s
.setLength(st
->TotalSize());
1900 st
->Serialise(s
.getMutableCStr(), s
.length());
1901 ok
= !st
->IsError();
1903 VStream::Destroy(st
);
1904 if (!ok
) Sys_Error("cannot read file '%s'", *Name
);
1905 Self
->Int
= new VScriptParser(*Name
, *s
);
1909 if (Name
.length() >= 2 && Name
[0] == '/' && Name
[1] == '/') {
1910 VStream
*strm
= FL_OpenFileRead(Name
);
1911 if (!strm
) Sys_Error("file '%s' not found", *Name
);
1912 Self
->Int
= new VScriptParser(*Name
, strm
);
1914 int num
= W_GetNumForFileName(Name
);
1915 //int num = W_IterateFile(-1, *Name);
1916 if (num
< 0) Sys_Error("file '%s' not found", *Name
);
1917 Self
->Int
= VScriptParser::NewWithLump(num
);
1919 #elif defined(VCC_STANDALONE_EXECUTOR)
1922 Self
->Int
= nullptr;
1924 VStream
*st
= fsysOpenFile(*Name
);
1925 if (!st
) Sys_Error("file '%s' not found", *Name
);
1928 if (st
->TotalSize() > 0) {
1929 s
.setLength(st
->TotalSize());
1930 st
->Serialise(s
.getMutableCStr(), s
.length());
1931 ok
= !st
->IsError();
1933 VStream::Destroy(st
);
1934 if (!ok
) Sys_Error("cannot read file '%s'", *Name
);
1935 Self
->Int
= new VScriptParser(*Name
, *s
);
1937 Sys_Error("file '%s' not found", *Name
);
1941 IMPLEMENT_FUNCTION(VScriptsParser
, OpenString
) {
1944 vobjGetParamSelf(Name
, s
);
1947 Self
->Int
= nullptr;
1949 Self
->Int
= new VScriptParser(*Name
, *s
);
1952 IMPLEMENT_FUNCTION(VScriptsParser
, get_String
) {
1954 Self
->CheckInterface();
1955 RET_STR(Self
->Int
->String
);
1958 IMPLEMENT_FUNCTION(VScriptsParser
, get_Number
) {
1960 Self
->CheckInterface();
1961 RET_INT(Self
->Int
->Number
);
1964 IMPLEMENT_FUNCTION(VScriptsParser
, get_Float
) {
1966 Self
->CheckInterface();
1967 RET_FLOAT(Self
->Int
->Float
);
1970 IMPLEMENT_FUNCTION(VScriptsParser
, get_Crossed
) {
1972 Self
->CheckInterface();
1973 RET_BOOL(Self
->Int
->Crossed
);
1976 IMPLEMENT_FUNCTION(VScriptsParser
, get_Quoted
) {
1978 Self
->CheckInterface();
1979 RET_BOOL(Self
->Int
->QuotedString
);
1982 IMPLEMENT_FUNCTION(VScriptsParser
, get_SourceLump
) {
1984 Self
->CheckInterface();
1985 RET_BOOL(Self
->Int
->SourceLump
);
1988 IMPLEMENT_FUNCTION(VScriptsParser
, set_SourceLump
) {
1990 vobjGetParamSelf(value
);
1991 Self
->CheckInterface();
1992 Self
->Int
->SourceLump
= value
;
1995 IMPLEMENT_FUNCTION(VScriptsParser
, get_Escape
) {
1997 Self
->CheckInterface();
1998 RET_BOOL(Self
->Int
->IsEscape());
2001 IMPLEMENT_FUNCTION(VScriptsParser
, set_Escape
) {
2003 vobjGetParamSelf(value
);
2004 Self
->CheckInterface();
2005 Self
->Int
->SetEscape(value
);
2008 IMPLEMENT_FUNCTION(VScriptsParser
, get_CMode
) {
2010 Self
->CheckInterface();
2011 RET_BOOL(Self
->Int
->IsCMode());
2014 IMPLEMENT_FUNCTION(VScriptsParser
, set_CMode
) {
2016 vobjGetParamSelf(value
);
2017 Self
->CheckInterface();
2018 Self
->Int
->SetCMode(value
);
2021 IMPLEMENT_FUNCTION(VScriptsParser
, get_AllowNumSign
) {
2023 Self
->CheckInterface();
2024 RET_BOOL(Self
->Int
->IsAllowNumSign());
2027 IMPLEMENT_FUNCTION(VScriptsParser
, set_AllowNumSign
) {
2029 vobjGetParamSelf(value
);
2030 Self
->CheckInterface();
2031 Self
->Int
->SetAllowNumSign(value
);
2034 IMPLEMENT_FUNCTION(VScriptsParser
, get_EDGEMode
) {
2036 Self
->CheckInterface();
2037 RET_BOOL(Self
->Int
->IsEDGEMode());
2040 IMPLEMENT_FUNCTION(VScriptsParser
, set_EDGEMode
) {
2042 vobjGetParamSelf(value
);
2043 Self
->CheckInterface();
2044 Self
->Int
->SetEDGEMode(value
);
2047 IMPLEMENT_FUNCTION(VScriptsParser
, get_SemicolonComments
) {
2049 Self
->CheckInterface();
2050 RET_BOOL(Self
->Int
->IsSemicolonComments());
2053 IMPLEMENT_FUNCTION(VScriptsParser
, set_SemicolonComments
) {
2055 vobjGetParamSelf(value
);
2056 Self
->CheckInterface();
2057 Self
->Int
->SetSemicolonComments(value
);
2060 IMPLEMENT_FUNCTION(VScriptsParser
, get_HashComments
) {
2062 Self
->CheckInterface();
2063 RET_BOOL(Self
->Int
->IsHashComments());
2066 IMPLEMENT_FUNCTION(VScriptsParser
, set_HashComments
) {
2068 vobjGetParamSelf(value
);
2069 Self
->CheckInterface();
2070 Self
->Int
->SetHashComments(value
);
2073 IMPLEMENT_FUNCTION(VScriptsParser
, get_EndOfText
) {
2075 Self
->CheckInterface();
2076 RET_BOOL(Self
->Int
->End
);
2079 IMPLEMENT_FUNCTION(VScriptsParser
, IsText
) {
2081 Self
->CheckInterface();
2082 RET_BOOL(Self
->Int
->IsText());
2085 IMPLEMENT_FUNCTION(VScriptsParser
, IsAtEol
) {
2087 Self
->CheckInterface();
2088 RET_BOOL(Self
->Int
->IsAtEol());
2091 IMPLEMENT_FUNCTION(VScriptsParser
, IsCMode
) {
2093 Self
->CheckInterface();
2094 RET_BOOL(Self
->Int
->IsCMode());
2097 IMPLEMENT_FUNCTION(VScriptsParser
, SetCMode
) {
2099 vobjGetParamSelf(On
);
2100 Self
->CheckInterface();
2101 Self
->Int
->SetCMode(On
);
2104 IMPLEMENT_FUNCTION(VScriptsParser
, IsAllowNumSign
) {
2106 Self
->CheckInterface();
2107 RET_BOOL(Self
->Int
->IsAllowNumSign());
2110 IMPLEMENT_FUNCTION(VScriptsParser
, SetAllowNumSign
) {
2112 vobjGetParamSelf(On
);
2113 Self
->CheckInterface();
2114 Self
->Int
->SetAllowNumSign(On
);
2117 IMPLEMENT_FUNCTION(VScriptsParser
, IsEscape
) {
2119 Self
->CheckInterface();
2120 RET_BOOL(Self
->Int
->IsEscape());
2123 IMPLEMENT_FUNCTION(VScriptsParser
, SetEscape
) {
2125 vobjGetParamSelf(On
);
2126 Self
->CheckInterface();
2127 Self
->Int
->SetEscape(On
);
2130 IMPLEMENT_FUNCTION(VScriptsParser
, AtEnd
) {
2132 Self
->CheckInterface();
2133 RET_BOOL(Self
->Int
->AtEnd());
2136 IMPLEMENT_FUNCTION(VScriptsParser
, GetString
) {
2138 Self
->CheckInterface();
2139 RET_BOOL(Self
->Int
->GetString());
2142 IMPLEMENT_FUNCTION(VScriptsParser
, ExpectLoneChar
) {
2144 Self
->CheckInterface();
2145 Self
->Int
->ExpectLoneChar();
2148 #if !defined(VCC_STANDALONE_EXECUTOR)
2149 IMPLEMENT_FUNCTION(VScriptsParser
, ExpectColor
) {
2151 Self
->CheckInterface();
2152 RET_INT(Self
->Int
->ExpectColor());
2156 IMPLEMENT_FUNCTION(VScriptsParser
, ExpectString
) {
2158 Self
->CheckInterface();
2159 Self
->Int
->ExpectString();
2162 IMPLEMENT_FUNCTION(VScriptsParser
, Check
) {
2164 vobjGetParamSelf(Text
);
2165 Self
->CheckInterface();
2166 RET_BOOL(Self
->Int
->Check(*Text
));
2169 IMPLEMENT_FUNCTION(VScriptsParser
, CheckStartsWith
) {
2171 vobjGetParamSelf(Text
);
2172 Self
->CheckInterface();
2173 RET_BOOL(Self
->Int
->CheckStartsWith(*Text
));
2176 IMPLEMENT_FUNCTION(VScriptsParser
, Expect
) {
2178 vobjGetParamSelf(Text
);
2179 Self
->CheckInterface();
2180 Self
->Int
->Expect(*Text
);
2183 IMPLEMENT_FUNCTION(VScriptsParser
, CheckIdentifier
) {
2185 Self
->CheckInterface();
2186 RET_BOOL(Self
->Int
->CheckIdentifier());
2189 IMPLEMENT_FUNCTION(VScriptsParser
, ExpectIdentifier
) {
2191 Self
->CheckInterface();
2192 Self
->Int
->ExpectIdentifier();
2195 IMPLEMENT_FUNCTION(VScriptsParser
, CheckNumber
) {
2196 VOptParamBool
withSign(false);
2197 vobjGetParamSelf(withSign
);
2198 Self
->CheckInterface();
2199 RET_BOOL(withSign
? Self
->Int
->CheckNumberWithSign() : Self
->Int
->CheckNumber());
2202 IMPLEMENT_FUNCTION(VScriptsParser
, ExpectNumber
) {
2203 VOptParamBool
withSign(false);
2204 vobjGetParamSelf(withSign
);
2205 Self
->CheckInterface();
2206 if (withSign
) Self
->Int
->ExpectNumberWithSign(); else Self
->Int
->ExpectNumber();
2209 IMPLEMENT_FUNCTION(VScriptsParser
, CheckFloat
) {
2210 VOptParamBool
withSign(false);
2211 vobjGetParamSelf(withSign
);
2212 Self
->CheckInterface();
2213 RET_BOOL(withSign
? Self
->Int
->CheckFloatWithSign() : Self
->Int
->CheckFloat());
2216 IMPLEMENT_FUNCTION(VScriptsParser
, ExpectFloat
) {
2217 VOptParamBool
withSign(false);
2218 vobjGetParamSelf(withSign
);
2219 Self
->CheckInterface();
2220 if (withSign
) Self
->Int
->ExpectFloatWithSign(); else Self
->Int
->ExpectFloat();
2223 IMPLEMENT_FUNCTION(VScriptsParser
, ResetQuoted
) {
2225 Self
->CheckInterface();
2226 Self
->Int
->ResetQuoted();
2229 IMPLEMENT_FUNCTION(VScriptsParser
, ResetCrossed
) {
2231 Self
->CheckInterface();
2232 Self
->Int
->ResetCrossed();
2235 IMPLEMENT_FUNCTION(VScriptsParser
, SkipBracketed
) {
2236 VOptParamBool
bracketEaten(false);
2237 vobjGetParamSelf(bracketEaten
);
2238 Self
->CheckInterface();
2239 Self
->Int
->SkipBracketed(bracketEaten
);
2242 IMPLEMENT_FUNCTION(VScriptsParser
, SkipLine
) {
2244 Self
->CheckInterface();
2245 Self
->Int
->SkipLine();
2248 IMPLEMENT_FUNCTION(VScriptsParser
, UnGet
) {
2250 Self
->CheckInterface();
2254 IMPLEMENT_FUNCTION(VScriptsParser
, FileName
) {
2256 Self
->CheckInterface();
2257 RET_STR(Self
->Int
->GetScriptName());
2260 IMPLEMENT_FUNCTION(VScriptsParser
, CurrLine
) {
2262 Self
->CheckInterface();
2263 RET_INT(Self
->Int
->Line
);
2266 IMPLEMENT_FUNCTION(VScriptsParser
, TokenLine
) {
2268 Self
->CheckInterface();
2269 RET_INT(Self
->Int
->TokLine
);
2272 IMPLEMENT_FUNCTION(VScriptsParser
, ScriptError
) {
2273 VStr Msg
= PF_FormatString();
2275 Self
->CheckInterface();
2276 Self
->Int
->Error(*Msg
);
2279 IMPLEMENT_FUNCTION(VScriptsParser
, ScriptMessage
) {
2280 VStr Msg
= PF_FormatString();
2282 Self
->CheckInterface();
2283 Self
->Int
->Message(*Msg
);
2286 IMPLEMENT_FUNCTION(VScriptsParser
, SavePos
) {
2287 VScriptSavedPos
*pos
;
2288 vobjGetParamSelf(pos
);
2289 Self
->CheckInterface();
2290 if (pos
) *pos
= Self
->Int
->SavePos();
2293 IMPLEMENT_FUNCTION(VScriptsParser
, RestorePos
) {
2294 VScriptSavedPos
*pos
;
2295 vobjGetParamSelf(pos
);
2296 Self
->CheckInterface();
2297 if (pos
) Self
->Int
->RestorePos(*pos
);
2300 #if !defined(VCC_STANDALONE_EXECUTOR)
2301 //static native final int FindRelativeIncludeLump (int srclump, string fname);
2302 IMPLEMENT_FUNCTION(VScriptsParser
, FindRelativeIncludeLump
) {
2305 vobjGetParamSelf(srclump
, fname
);
2306 RET_INT(VScriptParser::FindRelativeIncludeLump(srclump
, fname
));
2309 //static native final int FindIncludeLump (int srclump, string fname);
2310 IMPLEMENT_FUNCTION(VScriptsParser
, FindIncludeLump
) {
2313 vobjGetParamSelf(srclump
, fname
);
2314 RET_INT(VScriptParser::FindIncludeLump(srclump
, fname
));