2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
//**************************************************************************
|
|
|
|
//**
|
|
|
|
//** sc_man.c : Heretic 2 : Raven Software, Corp.
|
|
|
|
//**
|
|
|
|
//** $RCSfile: sc_man.c,v $
|
|
|
|
//** $Revision: 1.3 $
|
|
|
|
//** $Date: 96/01/06 03:23:43 $
|
|
|
|
//** $Author: bgokey $
|
|
|
|
//**
|
|
|
|
//**************************************************************************
|
|
|
|
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "doomtype.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "sc_man.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "m_misc.h"
|
|
|
|
#include "templates.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "v_text.h"
|
|
|
|
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner Constructor
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FScanner::FScanner()
|
|
|
|
{
|
|
|
|
ScriptOpen = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner Destructor
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FScanner::~FScanner()
|
|
|
|
{
|
|
|
|
// Humm... Nothing to do in here.
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner Copy Constructor
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FScanner::FScanner(const FScanner &other)
|
|
|
|
{
|
|
|
|
ScriptOpen = false;
|
|
|
|
*this = other;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner OpenLumpNum Constructor
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FScanner::FScanner(int lumpnum)
|
|
|
|
{
|
|
|
|
ScriptOpen = false;
|
|
|
|
OpenLumpNum(lumpnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: operator =
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FScanner &FScanner::operator=(const FScanner &other)
|
|
|
|
{
|
|
|
|
if (this == &other)
|
|
|
|
{
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
if (!other.ScriptOpen)
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy protected members
|
|
|
|
ScriptOpen = true;
|
|
|
|
ScriptName = other.ScriptName;
|
|
|
|
ScriptBuffer = other.ScriptBuffer;
|
|
|
|
ScriptPtr = other.ScriptPtr;
|
|
|
|
ScriptEndPtr = other.ScriptEndPtr;
|
|
|
|
AlreadyGot = other.AlreadyGot;
|
|
|
|
AlreadyGotLine = other.AlreadyGotLine;
|
|
|
|
LastGotToken = other.LastGotToken;
|
|
|
|
LastGotPtr = other.LastGotPtr;
|
|
|
|
LastGotLine = other.LastGotLine;
|
|
|
|
CMode = other.CMode;
|
|
|
|
Escape = other.Escape;
|
|
|
|
StateMode = other.StateMode;
|
2016-03-03 21:34:42 +00:00
|
|
|
StateOptions = other.StateOptions;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
// Copy public members
|
|
|
|
if (other.String == other.StringBuffer)
|
|
|
|
{
|
|
|
|
memcpy(StringBuffer, other.StringBuffer, sizeof(StringBuffer));
|
|
|
|
BigStringBuffer = "";
|
|
|
|
String = StringBuffer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Past practice means the string buffer must be writeable, which
|
|
|
|
// removes some of the benefit from using an FString to store
|
|
|
|
// the big string buffer.
|
|
|
|
BigStringBuffer = other.BigStringBuffer;
|
|
|
|
String = BigStringBuffer.LockBuffer();
|
|
|
|
}
|
|
|
|
StringLen = other.StringLen;
|
|
|
|
TokenType = other.TokenType;
|
|
|
|
Number = other.Number;
|
|
|
|
Float = other.Float;
|
|
|
|
Name = other.Name;
|
|
|
|
Line = other.Line;
|
|
|
|
End = other.End;
|
|
|
|
Crossed = other.Crossed;
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: Open
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::Open (const char *name)
|
|
|
|
{
|
|
|
|
int lump = Wads.CheckNumForFullName(name, true);
|
|
|
|
if (lump == -1)
|
|
|
|
{
|
|
|
|
I_Error("Could not find script lump '%s'\n", name);
|
|
|
|
}
|
|
|
|
OpenLumpNum(lump);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: OpenFile
|
|
|
|
//
|
|
|
|
// Loads a script from a file. Uses new/delete for memory allocation.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::OpenFile (const char *name)
|
|
|
|
{
|
|
|
|
BYTE *filebuf;
|
|
|
|
int filesize;
|
|
|
|
|
|
|
|
Close ();
|
|
|
|
filesize = M_ReadFile (name, &filebuf);
|
|
|
|
ScriptBuffer = FString((const char *)filebuf, filesize);
|
|
|
|
delete[] filebuf;
|
|
|
|
ScriptName = name; // This is used for error messages so the full file name is preferable
|
|
|
|
LumpNum = -1;
|
|
|
|
PrepareScript ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: OpenMem
|
|
|
|
//
|
|
|
|
// Prepares a script that is already in memory for parsing. The memory is
|
|
|
|
// copied, so you can do whatever you want with it after opening it.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::OpenMem (const char *name, const char *buffer, int size)
|
|
|
|
{
|
|
|
|
OpenString(name, FString(buffer, size));
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: OpenString
|
|
|
|
//
|
|
|
|
// Like OpenMem, but takes a string directly.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::OpenString (const char *name, FString buffer)
|
|
|
|
{
|
|
|
|
Close ();
|
|
|
|
ScriptBuffer = buffer;
|
|
|
|
ScriptName = name;
|
|
|
|
LumpNum = -1;
|
|
|
|
PrepareScript ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: OpenLumpNum
|
|
|
|
//
|
|
|
|
// Loads a script from the lump directory
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner :: OpenLumpNum (int lump)
|
|
|
|
{
|
|
|
|
Close ();
|
|
|
|
{
|
|
|
|
FMemLump mem = Wads.ReadLump(lump);
|
|
|
|
ScriptBuffer = mem.GetString();
|
|
|
|
}
|
|
|
|
ScriptName = Wads.GetLumpFullPath(lump);
|
|
|
|
LumpNum = lump;
|
|
|
|
PrepareScript ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: PrepareScript
|
|
|
|
//
|
|
|
|
// Prepares a script for parsing.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::PrepareScript ()
|
|
|
|
{
|
|
|
|
// The scanner requires the file to end with a '\n', so add one if
|
|
|
|
// it doesn't already.
|
|
|
|
if (ScriptBuffer.Len() == 0 || ScriptBuffer[ScriptBuffer.Len() - 1] != '\n')
|
|
|
|
{
|
|
|
|
// If the last character in the buffer is a null character, change
|
|
|
|
// it to a newline. Otherwise, append a newline to the end.
|
|
|
|
if (ScriptBuffer.Len() > 0 && ScriptBuffer[ScriptBuffer.Len() - 1] == '\0')
|
|
|
|
{
|
|
|
|
ScriptBuffer.LockBuffer()[ScriptBuffer.Len() - 1] = '\n';
|
|
|
|
ScriptBuffer.UnlockBuffer();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ScriptBuffer += '\n';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ScriptPtr = &ScriptBuffer[0];
|
|
|
|
ScriptEndPtr = &ScriptBuffer[ScriptBuffer.Len()];
|
|
|
|
Line = 1;
|
|
|
|
End = false;
|
|
|
|
ScriptOpen = true;
|
|
|
|
String = StringBuffer;
|
|
|
|
AlreadyGot = false;
|
|
|
|
LastGotToken = false;
|
|
|
|
LastGotPtr = NULL;
|
|
|
|
LastGotLine = 1;
|
|
|
|
CMode = false;
|
|
|
|
Escape = true;
|
|
|
|
StateMode = 0;
|
2016-03-03 21:34:42 +00:00
|
|
|
StateOptions = false;
|
2016-03-01 15:47:10 +00:00
|
|
|
StringBuffer[0] = '\0';
|
|
|
|
BigStringBuffer = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: Close
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::Close ()
|
|
|
|
{
|
|
|
|
ScriptOpen = false;
|
|
|
|
ScriptBuffer = "";
|
|
|
|
BigStringBuffer = "";
|
|
|
|
StringBuffer[0] = '\0';
|
|
|
|
String = StringBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: SavePos
|
|
|
|
//
|
|
|
|
// Saves the current script location for restoration later
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
const FScanner::SavedPos FScanner::SavePos ()
|
|
|
|
{
|
|
|
|
SavedPos pos;
|
|
|
|
|
|
|
|
CheckOpen ();
|
|
|
|
if (End)
|
|
|
|
{
|
|
|
|
pos.SavedScriptPtr = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pos.SavedScriptPtr = ScriptPtr;
|
|
|
|
}
|
|
|
|
pos.SavedScriptLine = Line;
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: RestorePos
|
|
|
|
//
|
|
|
|
// Restores the previously saved script location
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::RestorePos (const FScanner::SavedPos &pos)
|
|
|
|
{
|
|
|
|
if (pos.SavedScriptPtr)
|
|
|
|
{
|
|
|
|
ScriptPtr = pos.SavedScriptPtr;
|
|
|
|
Line = pos.SavedScriptLine;
|
|
|
|
End = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
End = true;
|
|
|
|
}
|
|
|
|
AlreadyGot = false;
|
|
|
|
LastGotToken = false;
|
|
|
|
Crossed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: isText
|
|
|
|
//
|
|
|
|
// Checks if this is a text file.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::isText()
|
|
|
|
{
|
|
|
|
for(unsigned int i=0;i<ScriptBuffer.Len();i++)
|
|
|
|
{
|
|
|
|
int c = ScriptBuffer[i];
|
|
|
|
if (c < ' ' && c != '\n' && c != '\r' && c != '\t') return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: SetCMode
|
|
|
|
//
|
|
|
|
// Enables/disables C mode. In C mode, more characters are considered to
|
|
|
|
// be whole words than in non-C mode.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::SetCMode (bool cmode)
|
|
|
|
{
|
|
|
|
CMode = cmode;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: SetEscape
|
|
|
|
//
|
|
|
|
// Turns the escape sequence \" in strings on or off. If it's off, that
|
|
|
|
// means you can't include quotation marks inside strings.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::SetEscape (bool esc)
|
|
|
|
{
|
|
|
|
Escape = esc;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: SetStateMode
|
|
|
|
//
|
|
|
|
// Enters state mode. This mode is very permissive for identifiers, which
|
|
|
|
// it returns as TOK_NonWhitespace. The only character sequences that are
|
|
|
|
// not returned as such are these:
|
|
|
|
//
|
|
|
|
// * stop
|
|
|
|
// * wait
|
|
|
|
// * fail
|
|
|
|
// * loop
|
|
|
|
// * goto - Automatically exits state mode after it's seen.
|
|
|
|
// * :
|
|
|
|
// * ;
|
|
|
|
// * } - Automatically exits state mode after it's seen.
|
|
|
|
//
|
2016-03-03 21:34:42 +00:00
|
|
|
// Quoted strings are returned as TOK_NonWhitespace, minus the quotes. Once
|
|
|
|
// two consecutive sequences of TOK_NonWhitespace have been encountered
|
|
|
|
// (which would be the state's sprite and frame specifiers), nearly normal
|
|
|
|
// processing resumes, with the exception that various identifiers
|
|
|
|
// used for state options will be returned as tokens and not identifiers.
|
|
|
|
// This ends once a ';' or '{' character is encountered.
|
2016-03-01 15:47:10 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::SetStateMode(bool stately)
|
|
|
|
{
|
|
|
|
StateMode = stately ? 2 : 0;
|
2016-03-03 21:34:42 +00:00
|
|
|
StateOptions = stately;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner::ScanString
|
|
|
|
//
|
|
|
|
// Set tokens true if you want TokenType to be set.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::ScanString (bool tokens)
|
|
|
|
{
|
|
|
|
const char *marker, *tok;
|
|
|
|
bool return_val;
|
|
|
|
|
|
|
|
CheckOpen();
|
|
|
|
if (AlreadyGot)
|
|
|
|
{
|
|
|
|
AlreadyGot = false;
|
|
|
|
if (!tokens || LastGotToken)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
ScriptPtr = LastGotPtr;
|
|
|
|
Line = LastGotLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
Crossed = false;
|
|
|
|
if (ScriptPtr >= ScriptEndPtr)
|
|
|
|
{
|
|
|
|
End = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
LastGotPtr = ScriptPtr;
|
|
|
|
LastGotLine = Line;
|
|
|
|
|
|
|
|
// In case the generated scanner does not use marker, avoid compiler warnings.
|
|
|
|
marker;
|
|
|
|
#include "sc_man_scanner.h"
|
|
|
|
LastGotToken = tokens;
|
|
|
|
return return_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: GetString
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::GetString ()
|
|
|
|
{
|
|
|
|
return ScanString (false);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MustGetString
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::MustGetString (void)
|
|
|
|
{
|
|
|
|
if (FScanner::GetString() == false)
|
|
|
|
{
|
|
|
|
ScriptError ("Missing string (unexpected end of file).");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MustGetStringName
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::MustGetStringName (const char *name)
|
|
|
|
{
|
|
|
|
MustGetString ();
|
|
|
|
if (Compare (name) == false)
|
|
|
|
{
|
|
|
|
ScriptError ("Expected '%s', got '%s'.", name, String);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: CheckString
|
|
|
|
//
|
|
|
|
// Checks if the next token matches the specified string. Returns true if
|
|
|
|
// it does. If it doesn't, it ungets it and returns false.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::CheckString (const char *name)
|
|
|
|
{
|
|
|
|
if (GetString ())
|
|
|
|
{
|
|
|
|
if (Compare (name))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
UnGet ();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: GetToken
|
|
|
|
//
|
|
|
|
// Sets sc_Float, sc_Number, and sc_Name based on sc_TokenType.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::GetToken ()
|
|
|
|
{
|
|
|
|
if (ScanString (true))
|
|
|
|
{
|
|
|
|
if (TokenType == TK_NameConst)
|
|
|
|
{
|
|
|
|
Name = FName(String);
|
|
|
|
}
|
|
|
|
else if (TokenType == TK_IntConst)
|
|
|
|
{
|
|
|
|
char *stopper;
|
|
|
|
// Check for unsigned
|
|
|
|
if (String[StringLen - 1] == 'u' || String[StringLen - 1] == 'U' ||
|
|
|
|
String[StringLen - 2] == 'u' || String[StringLen - 2] == 'U')
|
|
|
|
{
|
|
|
|
TokenType = TK_UIntConst;
|
|
|
|
Number = strtoul(String, &stopper, 0);
|
|
|
|
Float = (unsigned)Number;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Number = strtol(String, &stopper, 0);
|
|
|
|
Float = Number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (TokenType == TK_FloatConst)
|
|
|
|
{
|
|
|
|
char *stopper;
|
|
|
|
Float = strtod(String, &stopper);
|
|
|
|
}
|
|
|
|
else if (TokenType == TK_StringConst)
|
|
|
|
{
|
|
|
|
StringLen = strbin(String);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MustGetAnyToken
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::MustGetAnyToken (void)
|
|
|
|
{
|
|
|
|
if (GetToken () == false)
|
|
|
|
{
|
|
|
|
ScriptError ("Missing token (unexpected end of file).");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: TokenMustBe
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::TokenMustBe (int token)
|
|
|
|
{
|
|
|
|
if (TokenType != token)
|
|
|
|
{
|
|
|
|
FString tok1 = TokenName(token);
|
|
|
|
FString tok2 = TokenName(TokenType, String);
|
|
|
|
ScriptError ("Expected %s but got %s instead.", tok1.GetChars(), tok2.GetChars());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MustGetToken
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::MustGetToken (int token)
|
|
|
|
{
|
|
|
|
MustGetAnyToken ();
|
|
|
|
TokenMustBe(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: CheckToken
|
|
|
|
//
|
|
|
|
// Checks if the next token matches the specified token. Returns true if
|
|
|
|
// it does. If it doesn't, it ungets it and returns false.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::CheckToken (int token)
|
|
|
|
{
|
|
|
|
if (GetToken ())
|
|
|
|
{
|
|
|
|
if (TokenType == token)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
UnGet ();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: GetNumber
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::GetNumber ()
|
|
|
|
{
|
|
|
|
char *stopper;
|
|
|
|
|
|
|
|
CheckOpen();
|
|
|
|
if (GetString())
|
|
|
|
{
|
|
|
|
if (strcmp (String, "MAXINT") == 0)
|
|
|
|
{
|
|
|
|
Number = INT_MAX;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Number = strtol (String, &stopper, 0);
|
|
|
|
if (*stopper != 0)
|
|
|
|
{
|
|
|
|
ScriptError ("SC_GetNumber: Bad numeric constant \"%s\".", String);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Float = Number;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MustGetNumber
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::MustGetNumber ()
|
|
|
|
{
|
|
|
|
if (GetNumber() == false)
|
|
|
|
{
|
|
|
|
ScriptError ("Missing integer (unexpected end of file).");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: CheckNumber
|
|
|
|
//
|
|
|
|
// similar to GetNumber but ungets the token if it isn't a number
|
|
|
|
// and does not print an error
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::CheckNumber ()
|
|
|
|
{
|
|
|
|
char *stopper;
|
|
|
|
|
|
|
|
if (GetString())
|
|
|
|
{
|
|
|
|
if (String[0] == 0)
|
|
|
|
{
|
|
|
|
UnGet();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (strcmp (String, "MAXINT") == 0)
|
|
|
|
{
|
|
|
|
Number = INT_MAX;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Number = strtol (String, &stopper, 0);
|
|
|
|
if (*stopper != 0)
|
|
|
|
{
|
|
|
|
UnGet();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Float = Number;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: CheckFloat
|
|
|
|
//
|
|
|
|
// [GRB] Same as SC_CheckNumber, only for floats
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::CheckFloat ()
|
|
|
|
{
|
|
|
|
char *stopper;
|
|
|
|
|
|
|
|
if (GetString())
|
|
|
|
{
|
|
|
|
if (String[0] == 0)
|
|
|
|
{
|
|
|
|
UnGet();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Float = strtod (String, &stopper);
|
|
|
|
if (*stopper != 0)
|
|
|
|
{
|
|
|
|
UnGet();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: GetFloat
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::GetFloat ()
|
|
|
|
{
|
|
|
|
char *stopper;
|
|
|
|
|
|
|
|
CheckOpen ();
|
|
|
|
if (GetString())
|
|
|
|
{
|
|
|
|
Float = strtod (String, &stopper);
|
|
|
|
if (*stopper != 0)
|
|
|
|
{
|
|
|
|
ScriptError ("SC_GetFloat: Bad numeric constant \"%s\".", String);
|
|
|
|
}
|
|
|
|
Number = (int)Float;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MustGetFloat
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::MustGetFloat ()
|
|
|
|
{
|
|
|
|
if (GetFloat() == false)
|
|
|
|
{
|
|
|
|
ScriptError ("Missing floating-point number (unexpected end of file).");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: UnGet
|
|
|
|
//
|
|
|
|
// Assumes there is a valid string in String.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::UnGet ()
|
|
|
|
{
|
|
|
|
AlreadyGot = true;
|
|
|
|
AlreadyGotLine = LastGotLine; // in case of an error we want the line of the last token.
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MatchString
|
|
|
|
//
|
|
|
|
// Returns the index of the first match to String from the passed
|
|
|
|
// array of strings, or -1 if not found.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
int FScanner::MatchString (const char * const *strings, size_t stride)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(stride % sizeof(const char*) == 0);
|
|
|
|
|
|
|
|
stride /= sizeof(const char*);
|
|
|
|
|
|
|
|
for (i = 0; *strings != NULL; i++)
|
|
|
|
{
|
|
|
|
if (Compare (*strings))
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
strings += stride;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: MustMatchString
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
int FScanner::MustMatchString (const char * const *strings, size_t stride)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = MatchString (strings, stride);
|
|
|
|
if (i == -1)
|
|
|
|
{
|
|
|
|
ScriptError ("Unknown keyword '%s'", String);
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: Compare
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
bool FScanner::Compare (const char *text)
|
|
|
|
{
|
|
|
|
return (stricmp (text, String) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: TokenName
|
|
|
|
//
|
|
|
|
// Returns the name of a token.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FString FScanner::TokenName (int token, const char *string)
|
|
|
|
{
|
|
|
|
static const char *const names[] =
|
|
|
|
{
|
|
|
|
#define xx(sym,str) str,
|
|
|
|
#include "sc_man_tokens.h"
|
|
|
|
};
|
|
|
|
|
|
|
|
FString work;
|
|
|
|
|
|
|
|
if (token > ' ' && token < 256)
|
|
|
|
{
|
|
|
|
work = '\'';
|
|
|
|
work += token;
|
|
|
|
work += '\'';
|
|
|
|
}
|
|
|
|
else if (token >= TK_Identifier && token < TK_LastToken)
|
|
|
|
{
|
|
|
|
work = names[token - TK_Identifier];
|
|
|
|
if (string != NULL && token >= TK_Identifier && token <= TK_FloatConst)
|
|
|
|
{
|
|
|
|
work += ' ';
|
|
|
|
char quote = (token == TK_StringConst) ? '"' : '\'';
|
|
|
|
work += quote;
|
|
|
|
work += string;
|
|
|
|
work += quote;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FString work;
|
|
|
|
work.Format ("Unknown(%d)", token);
|
|
|
|
return work;
|
|
|
|
}
|
|
|
|
return work;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner::GetMessageLine
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
int FScanner::GetMessageLine()
|
|
|
|
{
|
|
|
|
return AlreadyGot? AlreadyGotLine : Line;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner::ScriptError
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2016-04-11 08:46:30 +00:00
|
|
|
void FScanner::ScriptError (const char *message, ...)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
FString composed;
|
|
|
|
|
|
|
|
if (message == NULL)
|
|
|
|
{
|
|
|
|
composed = "Bad syntax.";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
va_list arglist;
|
|
|
|
va_start (arglist, message);
|
|
|
|
composed.VFormat (message, arglist);
|
|
|
|
va_end (arglist);
|
|
|
|
}
|
|
|
|
|
|
|
|
I_Error ("Script error, \"%s\" line %d:\n%s\n", ScriptName.GetChars(),
|
|
|
|
AlreadyGot? AlreadyGotLine : Line, composed.GetChars());
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner::ScriptMessage
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2016-04-11 08:46:30 +00:00
|
|
|
void FScanner::ScriptMessage (const char *message, ...)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
FString composed;
|
|
|
|
|
|
|
|
if (message == NULL)
|
|
|
|
{
|
|
|
|
composed = "Bad syntax.";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
va_list arglist;
|
|
|
|
va_start (arglist, message);
|
|
|
|
composed.VFormat (message, arglist);
|
|
|
|
va_end (arglist);
|
|
|
|
}
|
|
|
|
|
|
|
|
Printf (TEXTCOLOR_RED "Script error, \"%s\" line %d:\n" TEXTCOLOR_RED "%s\n", ScriptName.GetChars(),
|
|
|
|
AlreadyGot? AlreadyGotLine : Line, composed.GetChars());
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScanner :: CheckOpen
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FScanner::CheckOpen()
|
|
|
|
{
|
|
|
|
if (ScriptOpen == false)
|
|
|
|
{
|
|
|
|
I_FatalError ("SC_ call before SC_Open().");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// a class that remembers a parser position
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
int FScriptPosition::ErrorCounter;
|
2016-10-10 22:56:47 +00:00
|
|
|
bool FScriptPosition::StrictErrors; // makes all OPTERRPR messages real errors.
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
FScriptPosition::FScriptPosition(const FScriptPosition &other)
|
|
|
|
{
|
|
|
|
FileName = other.FileName;
|
|
|
|
ScriptLine = other.ScriptLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
FScriptPosition::FScriptPosition(FString fname, int line)
|
|
|
|
{
|
|
|
|
FileName = fname;
|
|
|
|
ScriptLine = line;
|
|
|
|
}
|
|
|
|
|
|
|
|
FScriptPosition::FScriptPosition(FScanner &sc)
|
|
|
|
{
|
|
|
|
FileName = sc.ScriptName;
|
|
|
|
ScriptLine = sc.GetMessageLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
FScriptPosition &FScriptPosition::operator=(const FScriptPosition &other)
|
|
|
|
{
|
|
|
|
FileName = other.FileName;
|
|
|
|
ScriptLine = other.ScriptLine;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FScriptPosition::Message
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2016-06-08 08:56:11 +00:00
|
|
|
CVAR(Bool, strictdecorate, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
|
|
|
|
2016-04-11 08:46:30 +00:00
|
|
|
void FScriptPosition::Message (int severity, const char *message, ...) const
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
FString composed;
|
|
|
|
|
2016-08-28 07:55:04 +00:00
|
|
|
if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && developer < DMSG_NOTIFY) return;
|
2016-06-08 08:56:11 +00:00
|
|
|
if (severity == MSG_OPTERROR)
|
|
|
|
{
|
2016-10-10 22:56:47 +00:00
|
|
|
severity = StrictErrors || strictdecorate ? MSG_ERROR : MSG_WARNING;
|
2016-06-08 08:56:11 +00:00
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
if (message == NULL)
|
|
|
|
{
|
|
|
|
composed = "Bad syntax.";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
va_list arglist;
|
|
|
|
va_start (arglist, message);
|
|
|
|
composed.VFormat (message, arglist);
|
|
|
|
va_end (arglist);
|
|
|
|
}
|
|
|
|
const char *type = "";
|
|
|
|
const char *color;
|
|
|
|
int level = PRINT_HIGH;
|
|
|
|
|
|
|
|
switch (severity)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
|
|
|
|
case MSG_WARNING:
|
|
|
|
type = "warning";
|
|
|
|
color = TEXTCOLOR_YELLOW;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_ERROR:
|
|
|
|
ErrorCounter++;
|
|
|
|
type = "error";
|
|
|
|
color = TEXTCOLOR_RED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_MESSAGE:
|
|
|
|
case MSG_DEBUG:
|
|
|
|
type = "message";
|
|
|
|
color = TEXTCOLOR_GREEN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_DEBUGLOG:
|
|
|
|
case MSG_LOG:
|
|
|
|
type = "message";
|
|
|
|
level = PRINT_LOG;
|
|
|
|
color = "";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MSG_FATAL:
|
|
|
|
I_Error ("Script error, \"%s\" line %d:\n%s\n",
|
|
|
|
FileName.GetChars(), ScriptLine, composed.GetChars());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Printf (level, "%sScript %s, \"%s\" line %d:\n%s%s\n",
|
|
|
|
color, type, FileName.GetChars(), ScriptLine, color, composed.GetChars());
|
|
|
|
}
|
|
|
|
|
|
|
|
|