qzdoom/src/fragglescript/t_prepro.cpp
2016-03-01 09:47:10 -06:00

445 lines
11 KiB
C++

// Emacs style mode select -*- C++ -*-
//----------------------------------------------------------------------------
//
// Copyright(C) 2000 Simon Howard
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//--------------------------------------------------------------------------
//
// Preprocessor.
//
// The preprocessor must be called when the script is first loaded.
// It performs 2 functions:
// 1: blank out comments (which could be misinterpreted)
// 2: makes a list of all the sections held within {} braces
// 3: 'dry' runs the script: goes thru each statement and
// sets the types of all the DFsSection's in the script
// 4: Saves locations of all goto() labels
//
// the system of DFsSection's is pretty horrible really, but it works
// and its probably the only way i can think of of saving scripts
// half-way thru running
//
// By Simon Howard
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
/* includes ************************/
#include "t_script.h"
#include "i_system.h"
#include "w_wad.h"
#include "farchive.h"
//==========================================================================
//
// {} sections
//
// during preprocessing all of the {} sections
// are found. these are stored in a hash table
// according to their offset in the script.
// functions here deal with creating new sections
// and finding them from a given offset.
//
//==========================================================================
IMPLEMENT_POINTY_CLASS(DFsSection)
DECLARE_POINTER(next)
END_POINTERS
//==========================================================================
//
//
//
//==========================================================================
void DFsSection::Serialize(FArchive &ar)
{
Super::Serialize(ar);
ar << type << start_index << end_index << loop_index << next;
}
//==========================================================================
//
//
//
//==========================================================================
char *DFsScript::SectionStart(const DFsSection *sec)
{
return data + sec->start_index;
}
//==========================================================================
//
//
//
//==========================================================================
char *DFsScript::SectionEnd(const DFsSection *sec)
{
return data + sec->end_index;
}
//==========================================================================
//
//
//
//==========================================================================
char *DFsScript::SectionLoop(const DFsSection *sec)
{
return data + sec->loop_index;
}
//==========================================================================
//
//
//
//==========================================================================
void DFsScript::ClearSections()
{
for(int i=0;i<SECTIONSLOTS;i++)
{
DFsSection * var = sections[i];
while(var)
{
DFsSection *next = var->next;
var->Destroy();
var = next;
}
sections[i] = NULL;
}
}
//==========================================================================
//
// create section
//
//==========================================================================
DFsSection *DFsScript::NewSection(const char *brace)
{
int n = section_hash(brace);
DFsSection *newsec = new DFsSection;
newsec->start_index = MakeIndex(brace);
newsec->next = sections[n];
sections[n] = newsec;
GC::WriteBarrier(this, newsec);
return newsec;
}
//==========================================================================
//
// find a Section from the location of the starting { brace
//
//==========================================================================
DFsSection *DFsScript::FindSectionStart(const char *brace)
{
int n = section_hash(brace);
DFsSection *current = sections[n];
// use the hash table: check the appropriate hash chain
while(current)
{
if(SectionStart(current) == brace) return current;
current = current->next;
}
return NULL; // not found
}
//==========================================================================
//
// find a Section from the location of the closing } brace
//
//==========================================================================
DFsSection *DFsScript::FindSectionEnd(const char *brace)
{
int n;
// hash table is no use, they are hashed according to
// the offset of the starting brace
// we have to go through every entry to find from the
// ending brace
for(n=0; n<SECTIONSLOTS; n++) // check all sections in all chains
{
DFsSection *current = sections[n];
while(current)
{
if(SectionEnd(current) == brace) return current; // found it
current = current->next;
}
}
return NULL; // not found
}
//==========================================================================
//
// preproocessor main loop
//
// This works by recursion. when a { opening
// brace is found, another instance of the
// function is called for the data inside
// the {} section.
// At the same time, the sections are noted
// down and hashed. Goto() labels are noted
// down, and comments are blanked out
//
//==========================================================================
char *DFsScript::ProcessFindChar(char *datap, char find)
{
while(*datap)
{
if(*datap==find) return datap;
if(*datap=='\"') // found a quote: ignore stuff in it
{
datap++;
while(*datap && *datap != '\"')
{
// escape sequence ?
if(*datap=='\\') datap++;
datap++;
}
// error: end of script in a constant
if(!*datap) return NULL;
}
// comments: blank out
if(*datap=='/' && *(datap+1)=='*') // /* -- */ comment
{
while(*datap && (*datap != '*' || *(datap+1) != '/') )
{
*datap=' '; datap++;
}
if(*datap)
*datap = *(datap+1) = ' '; // blank the last bit
else
{
// script terminated in comment
script_error("script terminated inside comment\n");
}
}
if(*datap=='/' && *(datap+1)=='/') // // -- comment
{
while(*datap != '\n')
{
*datap=' '; datap++; // blank out
}
}
/********** labels ****************/
// labels are also found during the
// preprocessing. these are of the form
//
// label_name:
//
// and are used for the goto function.
// goto labels are stored as variables.
if(*datap==':' && scriptnum != -1) // not in global scripts
{
char *labelptr = datap-1;
while(!isop(*labelptr)) labelptr--;
FString labelname(labelptr+1, strcspn(labelptr+1, ":"));
if (labelname.Len() == 0)
{
Printf(PRINT_BOLD,"Script %d: ':' encountrered in incorrect position!\n",scriptnum);
}
DFsVariable *newlabel = NewVariable(labelname, svt_label);
newlabel->value.i = MakeIndex(labelptr);
}
if(*datap=='{') // { -- } sections: add 'em
{
DFsSection *newsec = NewSection(datap);
newsec->type = st_empty;
// find the ending } and save
char * theend = ProcessFindChar(datap+1, '}');
if(!theend)
{ // brace not found
// This is fatal because it will cause a crash later
// if the game isn't terminated.
I_Error("Script %d: section error: no ending brace\n", scriptnum);
}
newsec->end_index = MakeIndex(theend);
// continue from the end of the section
datap = theend;
}
datap++;
}
return NULL;
}
//==========================================================================
//
// second stage parsing
//
// second stage preprocessing considers the script
// in terms of tokens rather than as plain data.
//
// we 'dry' run the script: go thru each statement and
// collect types for Sections
//
// this is an important thing to do, it cannot be done
// at runtime for 2 reasons:
// 1. gotos() jumping inside loops will pass thru
// the end of the loop
// 2. savegames. loading a script saved inside a
// loop will let it pass thru the loop
//
// this is basically a cut-down version of the normal
// parsing loop.
//
//==========================================================================
void DFsScript::DryRunScript()
{
char *end = data + len;
char *rover = data;
// allocate space for the tokens
FParser parse(this);
try
{
while(rover < end && *rover)
{
rover = parse.GetTokens(rover);
if(!parse.NumTokens) continue;
if(parse.Section && parse.TokenType[0] == function)
{
if(!strcmp(parse.Tokens[0], "if"))
{
parse.Section->type = st_if;
continue;
}
else if(!strcmp(parse.Tokens[0], "elseif")) // haleyjd: SoM's else code
{
parse.Section->type = st_elseif;
continue;
}
else if(!strcmp(parse.Tokens[0], "else"))
{
parse.Section->type = st_else;
continue;
}
else if(!strcmp(parse.Tokens[0], "while") ||
!strcmp(parse.Tokens[0], "for"))
{
parse.Section->type = st_loop;
parse.Section->loop_index = MakeIndex(parse.LineStart);
continue;
}
}
}
}
catch (CFsError err)
{
parse.ErrorMessage(err.msg);
}
}
//==========================================================================
//
// main preprocess function
//
//==========================================================================
void DFsScript::Preprocess()
{
len = (int)strlen(data);
ProcessFindChar(data, 0); // fill in everything
DryRunScript();
}
//==========================================================================
//
// FraggleScript allows 'including' of other lumps.
// we divert input from the current script (normally
// levelscript) to a seperate lump. This of course
// first needs to be preprocessed to remove comments
// etc.
//
// parse an 'include' lump
//
//==========================================================================
void DFsScript::ParseInclude(char *lumpname)
{
int lumpnum;
char *lump;
if((lumpnum = Wads.CheckNumForName(lumpname)) == -1)
{
I_Error("include lump '%s' not found!\n", lumpname);
return;
}
int lumplen=Wads.LumpLength(lumpnum);
lump=new char[lumplen+10];
Wads.ReadLump(lumpnum,lump);
lump[lumplen]=0;
// preprocess the include
// we assume that it does not include sections or labels or
// other nasty things
ProcessFindChar(lump, 0);
// now parse the lump
FParser parse(this);
parse.Run(lump, lump, lump+lumplen);
// free the lump
delete[] lump;
}