mirror of
https://github.com/DrBeef/Raze.git
synced 2024-12-14 06:31:17 +00:00
535 lines
16 KiB
C++
535 lines
16 KiB
C++
/*
|
|
**
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 2005-2016 Randy Heit
|
|
** Copyright 2005-2016 Christoph Oelckers
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
|
|
#define YYCTYPE unsigned char
|
|
#define YYCURSOR cursor
|
|
#define YYLIMIT limit
|
|
#define YYMARKER marker
|
|
#define YYFILL(n) {}
|
|
#if 0 // As long as the buffer ends with '\n', we need do nothing special for YYFILL.
|
|
// This buffer must be as large as the largest YYFILL call
|
|
YYCTYPE eofbuf[9];
|
|
#define YYFILL(n) \
|
|
{ if(!sc_End) { \
|
|
if(n == 2) { eofbuf[0] = *cursor; } \
|
|
else if(n >= 3 && n <= 9) { memcpy(eofbuf, cursor, n-1); } \
|
|
eofbuf[n-1] = '\n'; \
|
|
cursor = eofbuf; \
|
|
limit = eofbuf + n - 1; \
|
|
sc_End = true; } \
|
|
} \
|
|
assert(n <= sizeof eofbuf) // Semicolon intentionally omitted
|
|
#endif
|
|
|
|
//#define YYDEBUG(s,c) { Printf ("%d: %02x\n", s, c); }
|
|
#define YYDEBUG(s,c)
|
|
|
|
const char *cursor = ScriptPtr;
|
|
const char *limit = ScriptEndPtr;
|
|
|
|
std1:
|
|
tok = YYCURSOR;
|
|
std2:
|
|
/*!re2c
|
|
any = [\000-\377];
|
|
WSP = ([\000- ]\[\n]);
|
|
NWS = (any\[\000- ]);
|
|
O = [0-7];
|
|
D = [0-9];
|
|
L = [a-zA-Z_];
|
|
H = [a-fA-F0-9];
|
|
E = [Ee] [+-]? D+;
|
|
FS = [fF];
|
|
IS = [uUlL];
|
|
ESC = [\\] ([abcfnrtv?'"\\] | "x" H+ | O+);
|
|
|
|
TOK1 = [{}|=];
|
|
TOKC = [{}|=/`~!@#$%^&*()\[\]\\?\-=+;:<>,.];
|
|
|
|
STOP1 = (TOK1|["/;]);
|
|
STOPC = (TOKC|["]);
|
|
|
|
TOK2 = (NWS\STOP1);
|
|
TOKC2 = (NWS\STOPC);
|
|
*/
|
|
#define RET(x) TokenType = (x); goto normal_token;
|
|
if (tokens && StateMode != 0)
|
|
{
|
|
/*!re2c
|
|
"/*" { goto comment; } /* C comment */
|
|
"//" (any\"\n")* "\n" { goto newline; } /* C++ comment */
|
|
("#region"|"#endregion") (any\"\n")* "\n"
|
|
{ goto newline; } /* Region blocks [mxd] */
|
|
|
|
(["](([\\]["])|[^"])*["]) { goto string_const; }
|
|
'stop' { RET(TK_Stop); }
|
|
'wait' { RET(TK_Wait); }
|
|
'fail' { RET(TK_Fail); }
|
|
'loop' { RET(TK_Loop); }
|
|
'goto' { StateMode = 0; StateOptions = false; RET(TK_Goto); }
|
|
":" { RET(':'); }
|
|
";" { RET(';'); }
|
|
"}" { StateMode = 0; StateOptions = false; RET('}'); }
|
|
|
|
WSP+ { goto std1; }
|
|
"\n" { goto newline; }
|
|
|
|
TOKS = (NWS\[/":;}]);
|
|
TOKS* ([/] (TOKS\[*]) TOKS*)*
|
|
{ RET(TK_NonWhitespace); }
|
|
|
|
*/
|
|
}
|
|
else if (tokens) // A well-defined scanner, based on the c.re example.
|
|
{
|
|
/*!re2c
|
|
"/*" { goto comment; } /* C comment */
|
|
"//" (any\"\n")* "\n" { goto newline; } /* C++ comment */
|
|
("#region"|"#endregion") (any\"\n")* "\n"
|
|
{ goto newline; } /* Region blocks [mxd] */
|
|
|
|
/* C Keywords */
|
|
'break' { RET(TK_Break); }
|
|
'case' { RET(TK_Case); }
|
|
'const' { RET(TK_Const); }
|
|
'continue' { RET(TK_Continue); }
|
|
'default' { RET(TK_Default); }
|
|
'do' { RET(TK_Do); }
|
|
'else' { RET(TK_Else); }
|
|
'for' { RET(TK_For); }
|
|
'goto' { RET(TK_Goto); }
|
|
'if' { RET(TK_If); }
|
|
'return' { RET(TK_Return); }
|
|
'switch' { RET(TK_Switch); }
|
|
'until' { RET(TK_Until); }
|
|
'volatile' { RET(TK_Volatile); }
|
|
'while' { RET(TK_While); }
|
|
|
|
/* Type names */
|
|
'bool' { RET(TK_Bool); }
|
|
'float' { RET(TK_Float); }
|
|
'double' { RET(TK_Double); }
|
|
'char' { RET(TK_Char); }
|
|
'byte' { RET(TK_Byte); }
|
|
'sbyte' { RET(TK_SByte); }
|
|
'short' { RET(TK_Short); }
|
|
'ushort' { RET(TK_UShort); }
|
|
'int8' { RET(TK_Int8); }
|
|
'uint8' { RET(TK_UInt8); }
|
|
'int16' { RET(TK_Int16); }
|
|
'uint16' { RET(TK_UInt16); }
|
|
'int' { RET(TK_Int); }
|
|
'uint' { RET(TK_UInt); }
|
|
'long' { RET(TK_Long); }
|
|
'ulong' { RET(TK_ULong); }
|
|
'void' { RET(TK_Void); }
|
|
'struct' { RET(TK_Struct); }
|
|
'class' { RET(TK_Class); }
|
|
'mixin' { RET(TK_Mixin); }
|
|
'enum' { RET(TK_Enum); }
|
|
'name' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Name : TK_Identifier); }
|
|
'string' { RET(TK_String); }
|
|
'sound' { RET(TK_Sound); }
|
|
'state' { RET(TK_State); }
|
|
'color' { RET(TK_Color); }
|
|
'vector2' { RET(TK_Vector2); }
|
|
'vector3' { RET(TK_Vector3); }
|
|
'map' { RET(TK_Map); }
|
|
'array' { RET(TK_Array); }
|
|
'in' { RET(TK_In); }
|
|
'sizeof' { RET(TK_SizeOf); }
|
|
'alignof' { RET(TK_AlignOf); }
|
|
|
|
/* Other keywords from UnrealScript */
|
|
'abstract' { RET(TK_Abstract); }
|
|
'foreach' { RET(TK_ForEach); }
|
|
'true' { RET(TK_True); }
|
|
'false' { RET(TK_False); }
|
|
'none' { RET(TK_None); }
|
|
'auto' { RET(TK_Auto); }
|
|
'property' { RET(TK_Property); }
|
|
'flagdef' { RET(ParseVersion >= MakeVersion(3, 7, 0)? TK_FlagDef : TK_Identifier); }
|
|
'native' { RET(TK_Native); }
|
|
'var' { RET(TK_Var); }
|
|
'out' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Out : TK_Identifier); }
|
|
'static' { RET(TK_Static); }
|
|
'transient' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Transient : TK_Identifier); }
|
|
'final' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Final : TK_Identifier); }
|
|
'extend' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Extend : TK_Identifier); }
|
|
'protected' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Protected : TK_Identifier); }
|
|
'private' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Private : TK_Identifier); }
|
|
'dot' { RET(TK_Dot); }
|
|
'cross' { RET(TK_Cross); }
|
|
'virtual' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Virtual : TK_Identifier); }
|
|
'override' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Override : TK_Identifier); }
|
|
'vararg' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_VarArg : TK_Identifier); }
|
|
'ui' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_UI : TK_Identifier); }
|
|
'play' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_Play : TK_Identifier); }
|
|
'clearscope' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_ClearScope : TK_Identifier); }
|
|
'virtualscope' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_VirtualScope : TK_Identifier); }
|
|
'super' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Super : TK_Identifier); }
|
|
'stop' { RET(TK_Stop); }
|
|
'null' { RET(TK_Null); }
|
|
|
|
'is' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Is : TK_Identifier); }
|
|
'replaces' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Replaces : TK_Identifier); }
|
|
'states' { RET(TK_States); }
|
|
'meta' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Meta : TK_Identifier); }
|
|
'deprecated' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Deprecated : TK_Identifier); }
|
|
'version' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_Version : TK_Identifier); }
|
|
'action' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Action : TK_Identifier); }
|
|
'readonly' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_ReadOnly : TK_Identifier); }
|
|
'internal' { RET(ParseVersion >= MakeVersion(3, 4, 0)? TK_Internal : TK_Identifier); }
|
|
'let' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Let : TK_Identifier); }
|
|
|
|
/* Actor state options */
|
|
'bright' { RET(StateOptions ? TK_Bright : TK_Identifier); }
|
|
'fast' { RET(StateOptions ? TK_Fast : TK_Identifier); }
|
|
'slow' { RET(StateOptions ? TK_Slow : TK_Identifier); }
|
|
'nodelay' { RET(StateOptions ? TK_NoDelay : TK_Identifier); }
|
|
'canraise' { RET(StateOptions ? TK_CanRaise : TK_Identifier); }
|
|
'offset' { RET(StateOptions ? TK_Offset : TK_Identifier); }
|
|
'light' { RET(StateOptions ? TK_Light : TK_Identifier); }
|
|
|
|
/* other DECORATE top level keywords */
|
|
'#include' { RET(TK_Include); }
|
|
|
|
L (L|D)* { RET(TK_Identifier); }
|
|
|
|
("0" [xX] H+ IS?IS?) | ("0" D+ IS?IS?) | (D+ IS?IS?)
|
|
{ RET(TK_IntConst); }
|
|
|
|
(D+ E FS?) | (D* "." D+ E? FS?) | (D+ "." D* E? FS?)
|
|
{ RET(TK_FloatConst); }
|
|
|
|
(["](([\\]["])|[^"])*["])
|
|
{ goto string_const; }
|
|
|
|
(['] (any\[\n'])* ['])
|
|
{ RET(TK_NameConst); }
|
|
|
|
".." { RET(TK_DotDot); }
|
|
"..." { RET(TK_Ellipsis); }
|
|
">>>=" { RET(TK_URShiftEq); }
|
|
">>=" { RET(TK_RShiftEq); }
|
|
"<<=" { RET(TK_LShiftEq); }
|
|
"+=" { RET(TK_AddEq); }
|
|
"-=" { RET(TK_SubEq); }
|
|
"*=" { RET(TK_MulEq); }
|
|
"/=" { RET(TK_DivEq); }
|
|
"%=" { RET(TK_ModEq); }
|
|
"&=" { RET(TK_AndEq); }
|
|
"^=" { RET(TK_XorEq); }
|
|
"|=" { RET(TK_OrEq); }
|
|
">>>" { RET(TK_URShift); }
|
|
">>" { RET(TK_RShift); }
|
|
"<<" { RET(TK_LShift); }
|
|
"++" { RET(TK_Incr); }
|
|
"--" { RET(TK_Decr); }
|
|
"&&" { RET(TK_AndAnd); }
|
|
"||" { RET(TK_OrOr); }
|
|
"<=" { RET(TK_Leq); }
|
|
">=" { RET(TK_Geq); }
|
|
"==" { RET(TK_Eq); }
|
|
"!=" { RET(TK_Neq); }
|
|
"~==" { RET(TK_ApproxEq); }
|
|
"<>=" { RET(TK_LtGtEq); }
|
|
"**" { RET(TK_MulMul); }
|
|
"::" { RET(TK_ColonColon); }
|
|
"->" { RET(TK_Arrow); }
|
|
";" { StateOptions = false; RET(';'); }
|
|
"{" { StateOptions = false; RET('{'); }
|
|
"}" { RET('}'); }
|
|
"," { RET(','); }
|
|
":" { RET(':'); }
|
|
"=" { RET('='); }
|
|
"(" { RET('('); }
|
|
")" { RET(')'); }
|
|
"[" { RET('['); }
|
|
"]" { RET(']'); }
|
|
"." { RET('.'); }
|
|
"&" { RET('&'); }
|
|
"!" { RET('!'); }
|
|
"~" { RET('~'); }
|
|
"-" { RET('-'); }
|
|
"+" { RET('+'); }
|
|
"*" { RET('*'); }
|
|
"/" { RET('/'); }
|
|
"%" { RET('%'); }
|
|
"<" { RET('<'); }
|
|
">" { RET('>'); }
|
|
"^" { RET('^'); }
|
|
"|" { RET('|'); }
|
|
"?" { RET('?'); }
|
|
"#" { RET('#'); }
|
|
"@" { RET('@'); }
|
|
|
|
[ \t\v\f\r]+ { goto std1; }
|
|
"\n" { goto newline; }
|
|
any
|
|
{
|
|
ScriptError ("Unexpected character: %c (ASCII %d)\n", *tok, *tok);
|
|
goto std1;
|
|
}
|
|
*/
|
|
}
|
|
if (!CMode) // The classic Hexen scanner.
|
|
{
|
|
/*!re2c
|
|
"/*" { goto comment; } /* C comment */
|
|
("//"|";") (any\"\n")* "\n" { goto newline; } /* C++/Hexen comment */
|
|
("#region"|"#endregion") (any\"\n")* "\n"
|
|
{ goto newline; } /* Region blocks [mxd] */
|
|
|
|
WSP+ { goto std1; } /* whitespace */
|
|
"\n" { goto newline; }
|
|
"\"" { goto string; }
|
|
|
|
TOK1 { goto normal_token; }
|
|
|
|
/* Regular tokens may contain /, but they must not contain comment starts */
|
|
TOK2* ([/] (TOK2\[*]) TOK2*)* { goto normal_token; }
|
|
|
|
any { goto normal_token; } /* unknown character */
|
|
*/
|
|
}
|
|
else // A modified Hexen scanner for DECORATE.
|
|
{
|
|
/*!re2c
|
|
"/*" { goto comment; } /* C comment */
|
|
"//" (any\"\n")* "\n" { goto newline; } /* C++ comment */
|
|
("#region"|"#endregion") (any\"\n")* "\n"
|
|
{ goto newline; } /* Region blocks [mxd] */
|
|
|
|
WSP+ { goto std1; } /* whitespace */
|
|
"\n" { goto newline; }
|
|
"\"" { goto string; }
|
|
|
|
[-] { goto negative_check; }
|
|
((D* [.] D+) | (D+ [.] D*)) { goto normal_token; } /* decimal number */
|
|
(D+ E FS?) | (D* "." D+ E? FS?) | (D+ "." D* E? FS?) { goto normal_token; } /* float with exponent */
|
|
"::" { goto normal_token; }
|
|
"&&" { goto normal_token; }
|
|
"==" { goto normal_token; }
|
|
"||" { goto normal_token; }
|
|
"<<" { goto normal_token; }
|
|
">>" { goto normal_token; }
|
|
TOKC { goto normal_token; }
|
|
TOKC2+ { goto normal_token; }
|
|
|
|
any { goto normal_token; } /* unknown character */
|
|
*/
|
|
}
|
|
|
|
negative_check:
|
|
// re2c doesn't have enough state to handle '-' as the start of a negative number
|
|
// and as its own token, so help it out a little.
|
|
TokenType = '-';
|
|
if (YYCURSOR >= YYLIMIT)
|
|
{
|
|
goto normal_token;
|
|
}
|
|
if (*YYCURSOR >= '0' && *YYCURSOR <= '9')
|
|
{
|
|
goto std2;
|
|
}
|
|
if (*YYCURSOR != '.' || YYCURSOR+1 >= YYLIMIT)
|
|
{
|
|
goto normal_token;
|
|
}
|
|
if (*(YYCURSOR+1) >= '0' && *YYCURSOR <= '9')
|
|
{
|
|
goto std2;
|
|
}
|
|
goto normal_token;
|
|
|
|
comment:
|
|
/*!re2c
|
|
"*/"
|
|
{
|
|
if (YYCURSOR >= YYLIMIT)
|
|
{
|
|
ScriptPtr = ScriptEndPtr;
|
|
return_val = false;
|
|
goto end;
|
|
}
|
|
goto std1;
|
|
}
|
|
"\n"
|
|
{
|
|
if (YYCURSOR >= YYLIMIT)
|
|
{
|
|
ScriptPtr = ScriptEndPtr;
|
|
return_val = false;
|
|
goto end;
|
|
}
|
|
Line++;
|
|
Crossed = true;
|
|
goto comment;
|
|
}
|
|
any { goto comment; }
|
|
*/
|
|
|
|
newline:
|
|
if (YYCURSOR >= YYLIMIT)
|
|
{
|
|
ScriptPtr = ScriptEndPtr;
|
|
return_val = false;
|
|
goto end;
|
|
}
|
|
Line++;
|
|
Crossed = true;
|
|
goto std1;
|
|
|
|
normal_token:
|
|
ScriptPtr = (YYCURSOR >= YYLIMIT) ? ScriptEndPtr : cursor;
|
|
StringLen = int(ScriptPtr - tok);
|
|
if (tokens && (TokenType == TK_StringConst || TokenType == TK_NameConst))
|
|
{
|
|
StringLen -= 2;
|
|
if (StringLen >= MAX_STRING_SIZE)
|
|
{
|
|
BigStringBuffer = FString(tok+1, StringLen);
|
|
}
|
|
else
|
|
{
|
|
memcpy (StringBuffer, tok+1, StringLen);
|
|
}
|
|
if (StateMode && TokenType == TK_StringConst)
|
|
{
|
|
TokenType = TK_NonWhitespace;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (StringLen >= MAX_STRING_SIZE)
|
|
{
|
|
BigStringBuffer = FString(tok, StringLen);
|
|
}
|
|
else
|
|
{
|
|
memcpy (StringBuffer, tok, StringLen);
|
|
}
|
|
}
|
|
if (tokens && StateMode)
|
|
{ // State mode is exited after two consecutive TK_NonWhitespace tokens
|
|
if (TokenType == TK_NonWhitespace)
|
|
{
|
|
StateMode--;
|
|
}
|
|
else
|
|
{
|
|
StateMode = 2;
|
|
}
|
|
}
|
|
if (StringLen < MAX_STRING_SIZE)
|
|
{
|
|
String = StringBuffer;
|
|
StringBuffer[StringLen] = '\0';
|
|
}
|
|
else
|
|
{
|
|
String = BigStringBuffer.LockBuffer();
|
|
}
|
|
return_val = true;
|
|
goto end;
|
|
|
|
string_const:
|
|
for (const char *c = tok; c < YYCURSOR; ++c)
|
|
{
|
|
if (*c == '\n') ++Line;
|
|
}
|
|
RET(TK_StringConst);
|
|
|
|
string:
|
|
if (YYLIMIT != ScriptEndPtr)
|
|
{
|
|
ScriptPtr = ScriptEndPtr;
|
|
return_val = false;
|
|
goto end;
|
|
}
|
|
ScriptPtr = cursor;
|
|
BigStringBuffer = "";
|
|
for (StringLen = 0; cursor < YYLIMIT; ++cursor)
|
|
{
|
|
if (Escape && *cursor == '\\' && *(cursor + 1) == '"')
|
|
{
|
|
cursor++;
|
|
}
|
|
else if (*cursor == '\r' && *(cursor + 1) == '\n')
|
|
{
|
|
cursor++; // convert CR-LF to simply LF
|
|
}
|
|
else if (*cursor == '"')
|
|
{
|
|
break;
|
|
}
|
|
if (*cursor == '\n')
|
|
{
|
|
if (CMode)
|
|
{
|
|
if (!Escape || StringLen == 0 || String[StringLen - 1] != '\\')
|
|
{
|
|
ScriptError ("Unterminated string constant");
|
|
}
|
|
else
|
|
{
|
|
StringLen--; // overwrite the \ character with \n
|
|
}
|
|
}
|
|
Line++;
|
|
Crossed = true;
|
|
}
|
|
if (StringLen == MAX_STRING_SIZE)
|
|
{
|
|
BigStringBuffer.AppendCStrPart(StringBuffer, StringLen);
|
|
StringLen = 0;
|
|
}
|
|
StringBuffer[StringLen++] = *cursor;
|
|
}
|
|
if (BigStringBuffer.IsNotEmpty() || StringLen == MAX_STRING_SIZE)
|
|
{
|
|
BigStringBuffer.AppendCStrPart(StringBuffer, StringLen);
|
|
String = BigStringBuffer.LockBuffer();
|
|
StringLen = int(BigStringBuffer.Len());
|
|
}
|
|
else
|
|
{
|
|
String = StringBuffer;
|
|
StringBuffer[StringLen] = '\0';
|
|
}
|
|
ScriptPtr = cursor + 1;
|
|
return_val = true;
|
|
end:
|