// 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 // //--------------------------------------------------------------------------- // /* includes ************************/ #include "t_script.h" #include "i_system.h" #include "w_wad.h" #include "serializer.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_CLASS(DFsSection, false, true) IMPLEMENT_POINTERS_START(DFsSection) IMPLEMENT_POINTER(next) IMPLEMENT_POINTERS_END //========================================================================== // // // //========================================================================== void DFsSection::Serialize(FSerializer &arc) { Super::Serialize(arc); arc("type", type) ("start_index", start_index) ("end_index", end_index) ("loop_index", loop_index) ("next", 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;inext; var->Destroy(); var = next; } sections[i] = NULL; } } //========================================================================== // // create section // //========================================================================== DFsSection *DFsScript::NewSection(const char *brace) { int n = section_hash(brace); DFsSection *newsec = Create(); 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; nnext; } } 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; }