warzone2100/lib/script/script_lexer.lpp

869 lines
21 KiB
Plaintext

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2012 Warzone 2100 Project
Warzone 2100 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.
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
%{
/*
* script.l
*
* Script file lexer.
*/
#include <physfs.h>
#include "lib/framework/frame.h"
#include "lib/framework/string_ext.h"
#include "lib/script/interpreter.h"
#include "lib/script/parse.h"
#include "lib/script/script.h"
/* Get the Yacc definitions */
#if defined (WZ_CC_MSVC)
#include "script_parser.hpp"
#else
#include "script_parser.h"
#endif
// fwrite declared with warn_unused_result, resulting in mysterious errors in "%%" on some distros.
static inline bool no_warn_unused_result(int ignore) { if (ignore) {} return true; }
#define fwrite(a, b, c, d) no_warn_unused_result(fwrite(a, b, c, d))
/* Maximum length for any TEXT value */
#ifndef YYLMAX
#define YYLMAX 255
#endif
#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION == 33
extern int scr_get_lineno(void);
extern FILE *scr_get_in(void);
extern FILE *scr_get_out(void);
extern int scr_get_leng(void);
extern char *scr_get_text(void);
extern void scr_set_lineno(int line_number);
extern void scr_set_in(FILE* in_str);
extern void scr_set_out(FILE* out_str);
extern int scr_get_debug(void);
extern void scr_set_debug(int bdebug);
extern int scr_lex_destroy(void);
#endif
/* Store for any string values */
static char aText[TEXT_BUFFERS][YYLMAX];
static UDWORD currText=0;
// Note if we are in a comment
static bool inComment = false;
/* FLEX include buffer stack */
static YY_BUFFER_STATE include_stack[MAX_SCR_INCLUDE_DEPTH];
/* FLEX define buffer stack */
static YY_BUFFER_STATE macro_stack[MAX_SCR_MACRO_DEPTH];
static SCR_MACRO scr_macro[MAX_SCR_MACROS];
/* Script include files stack */
static unsigned int scr_include_stack_ptr = 0;
static PHYSFS_file* pScrInputFiles[MAX_SCR_INCLUDE_DEPTH];
/* Line count stack used with script includes */
static unsigned int scrInclLine[MAX_SCR_INCLUDE_DEPTH];
/* Script defines stack */
static unsigned int scr_num_macros; /* Number of macros defined so far */
static int scr_macro_stack_ptr; /* Pointer to the current flex macro input buffer */
static char *pScrMacroBuffer[MAX_SCR_MACRO_DEPTH];
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
{ \
if (PHYSFS_eof(pScrInputFiles[scr_include_stack_ptr])) \
{ \
buf[0] = EOF; \
result = YY_NULL; \
} \
else \
{ \
result = PHYSFS_read(pScrInputFiles[scr_include_stack_ptr], buf, 1, max_size); \
if (result == -1) \
{ \
buf[0] = EOF; \
result = YY_NULL; \
} \
} \
}
static YYSTYPE dummy;
/* Get the token type for a variable symbol */
static SDWORD scriptGetVarToken(VAR_SYMBOL *psVar)
{
bool object;
// See if this is an object pointer
if (!asScrTypeTab || psVar->type < VAL_USERTYPESTART)
{
object = false;
}
else
{
object = asScrTypeTab[psVar->type - VAL_USERTYPESTART].accessType == AT_OBJECT;
}
if (psVar->storage == ST_OBJECT)
{
/* This is an object member variable */
if (object)
{
RULE("current variable is OBJ_OBJVAR");
return OBJ_OBJVAR;
}
else
{
switch (psVar->type)
{
case VAL_BOOL:
return BOOL_OBJVAR;
break;
case VAL_INT:
// case VAL_FLOAT:
RULE( "current variable is NUM_OBJVAR");
return NUM_OBJVAR;
break;
default:
RULE( "current variable is USER_OBJVAR");
return USER_OBJVAR;
break;
}
}
}
else if (psVar->dimensions > 0)
{
/* This is an array variable */
if (object)
{
return OBJ_ARRAY;
}
else
{
switch (psVar->type)
{
case VAL_BOOL:
return BOOL_ARRAY;
break;
case VAL_FLOAT:
return FLOAT_ARRAY;
break;
case VAL_INT:
return NUM_ARRAY;
break;
default:
return VAR_ARRAY;
break;
}
}
}
else
{
/* This is a standard variable */
if (object)
{
RULE( "current variable is OBJ_VAR");
return OBJ_VAR;
}
else
{
RULE( "ordinary var");
switch (psVar->type)
{
case VAL_BOOL:
return BOOL_VAR;
break;
case VAL_FLOAT:
RULE( "VAL_FLOAT");
return FLOAT_VAR;
break;
case VAL_INT:
return NUM_VAR;
break;
case VAL_STRING:
return STRING_VAR;
break;
default:
return VAR;
break;
}
}
}
}
/* Get the token type for a constant symbol */
static SDWORD scriptGetConstToken(CONST_SYMBOL *psConst)
{
bool object;
// See if this is an object constant
if (!asScrTypeTab || psConst->type < VAL_USERTYPESTART)
{
object = false;
}
else
{
object = asScrTypeTab[psConst->type - VAL_USERTYPESTART].accessType == AT_OBJECT;
}
switch (psConst->type)
{
case VAL_BOOL:
return BOOL_CONSTANT;
break;
// case VAL_FLOAT:
case VAL_INT:
return NUM_CONSTANT;
break;
case VAL_STRING:
return STRING_CONSTANT;
break;
default:
if (object)
{
//debug(LOG_SCRIPT, "scriptGetConstToken: OBJ_CONSTANT");
return OBJ_CONSTANT;
}
else
{
return USER_CONSTANT;
}
break;
}
}
/* Get the token type for a function symbol */
static SDWORD scriptGetFuncToken(FUNC_SYMBOL *psFunc)
{
bool object;
// See if this is an object pointer
if(psFunc->type >= VAL_USERTYPESTART)
{
object = asScrTypeTab[psFunc->type - VAL_USERTYPESTART].accessType == AT_OBJECT;
if (object)
{
return OBJ_FUNC;
}
}
switch (psFunc->type)
{
case VAL_BOOL:
return BOOL_FUNC;
break;
case VAL_FLOAT:
return FLOAT_FUNC;
break;
case VAL_INT:
return NUM_FUNC;
break;
case VAL_STRING:
return STRING_FUNC;
break;
case VAL_VOID:
return FUNC;
break;
default:
return USER_FUNC;
break;
}
}
/* Get the token type for a custom function symbol */
static SDWORD scriptGetCustomFuncToken(EVENT_SYMBOL *psFunc)
{
bool object;
// See if this is an object pointer
if (!asScrTypeTab || psFunc->retType < VAL_USERTYPESTART)
{
object = false;
}
else
{
object = asScrTypeTab[psFunc->retType - VAL_USERTYPESTART].accessType == AT_OBJECT;
}
if (object)
{
return OBJ_FUNC_CUST;
}
else
{
switch (psFunc->retType)
{
case VAL_BOOL:
return BOOL_FUNC_CUST;
break;
case VAL_FLOAT:
RULE( "current function is FLOAT_FUNC_CUST");
return FLOAT_FUNC_CUST;
break;
case VAL_INT:
RULE( "current function is NUM_FUNC_CUST");
return NUM_FUNC_CUST;
break;
case VAL_STRING:
return STRING_FUNC_CUST;
break;
case VAL_VOID:
return VOID_FUNC_CUST;
break;
default:
return USER_FUNC_CUST;
break;
}
}
}
/* Look up defined macro and return new define buffer */
static bool scriptLoopUpMacro(const char *pMacro, char **ppMacroBody)
{
unsigned int i;
for (i = 0; i < scr_num_macros; ++i)
{
if(strcmp(pMacro, &(scr_macro[i].scr_define_macro[0]) ) == 0)
{
*ppMacroBody = (char *)malloc(MAXSTRLEN);
strcpy( *ppMacroBody, &(scr_macro[i].scr_define_body[0]) ); //copy macro body into buffer so we can process it
//*pMacroBody = &(scr_macro[i].scr_define_body[0]); //return macro body
return true;
}
}
return false;
}
/* Return to the previous macto buffer */
static void popMacroStack(void)
{
ASSERT(scr_macro_stack_ptr <= MAX_SCR_MACRO_DEPTH,
"FLEX: macro stack pointer out of bounds: %d", scr_macro_stack_ptr);
/* Free current macro buffer */
free(pScrMacroBuffer[scr_macro_stack_ptr]);
pScrMacroBuffer[scr_macro_stack_ptr] = NULL;
/* Delete flex buffer */
yy_delete_buffer( YY_CURRENT_BUFFER );
/* Switch one level lower in the macro stack */
yy_switch_to_buffer( macro_stack[scr_macro_stack_ptr - 1] );
scr_macro_stack_ptr--;
}
/* Store macro name */
static void storeMacroName(const char *pMacroName)
{
ASSERT(scr_num_macros < MAX_SCR_MACROS,
"FLEX: macro count out of bounds: %u", scr_num_macros);
/* Make sure this name isn't already reserved */
if((scriptLookUpType(pMacroName, &dummy.tval)) ||
(scriptLookUpVariable(pMacroName, &dummy.vSymbol)) ||
(scriptLookUpConstant(pMacroName, &dummy.cSymbol)) ||
(scriptLookUpFunction(pMacroName, &dummy.fSymbol)) ||
(scriptLookUpCustomFunction(pMacroName, &dummy.eSymbol)) ||
(scriptLookUpTrigger(pMacroName, &dummy.tSymbol)) ||
(scriptLookUpEvent(pMacroName, &dummy.eSymbol)) ||
(scriptLookUpCallback(pMacroName, &dummy.cbSymbol)))
{
scr_error("FLEX: macro name '%s' is already reserved", pMacroName);
}
/* Store define macro */
sstrcpy(scr_macro[scr_num_macros].scr_define_macro, pMacroName);
}
/* Store macro body */
static void storeMacroBody(const char *pMacroBody)
{
ASSERT(scr_num_macros < MAX_SCR_MACROS,
"FLEX: macro count out of bounds: %u", scr_num_macros);
/* Store macro body */
sstrcpy(scr_macro[scr_num_macros].scr_define_body, pMacroBody);
scr_num_macros++;
}
/* include directive encountered - push current buffer, set up new one */
static void pushInclude(const char *pIncludePath)
{
PHYSFS_file* newInput;
if ( scr_include_stack_ptr >= MAX_SCR_INCLUDE_DEPTH ){
scr_error("FLEX: Includes nested too deeply" );
}
if(!PHYSFS_exists(pIncludePath)){
scr_error("FLEX: '%s' include doesn't exist", pIncludePath );
}
/* Open include */
newInput = PHYSFS_openRead(pIncludePath);
if(!newInput){
scr_error("FLEX: Couldn't open include: '%s'\n%s",
pIncludePath, PHYSFS_getLastError() );
}
/* Push current flex buffer */
include_stack[scr_include_stack_ptr] = YY_CURRENT_BUFFER;
scr_include_stack_ptr++;
ASSERT(scr_include_stack_ptr < MAX_SCR_INCLUDE_DEPTH,
"FLEX: scr_include_stack_ptr out of bounds: %u", scr_include_stack_ptr);
pScrInputFiles[scr_include_stack_ptr] = newInput;
}
%}
%option prefix="scr_"
%option nounput
%option yylineno
%option never-interactive
%x COMMENT
%x SLCOMMENT
%x QUOTE
%x _MACRO
%x _INCL
%x _INCL_END_QUOTE
%x _DEF_MACRO
%x _DEF_BODY_START
%x _DEF_BODY
_IDENTIFIER[a-zA-Z_][0-9_a-zA-Z_]*
%%
/* Match keywords */
wait return WAIT;
every return EVERY;
trigger return TRIGGER;
event return EVENT;
inactive return INACTIVE;
init return INITIALISE;
link return LINK;
ref return REF;
return return RET;
function return lexFUNCTION;
"("int")"|"("INT")" { RULE(yytext); return TO_INT_CAST; }
"("float")"|"("FLOAT")" { RULE(yytext); return TO_FLOAT_CAST; }
public { scr_lval.stype = ST_PUBLIC; return STORAGE; }
private { scr_lval.stype = ST_PRIVATE; return STORAGE; }
local { scr_lval.stype = ST_LOCAL; return STORAGE; }
while return WHILE;
if return IF;
else return ELSE;
exit return EXIT;
pause return PAUSE;
/* Match type keywords */
void|VOID { scr_lval.tval = VAL_VOID; return _VOID; }
string|STRING { scr_lval.tval = VAL_STRING; return TYPE; }
bool|BOOL { scr_lval.tval = VAL_BOOL; return TYPE; }
int|INT { scr_lval.tval = VAL_INT; return TYPE; }
float|FLOAT { scr_lval.tval = VAL_FLOAT; return TYPE; }
/* Match boolean values */
true|TRUE { scr_lval.bval = true; return BOOLEAN_T; }
false|FALSE { scr_lval.bval = false; return BOOLEAN_T; }
/* Match increment/decrement operators */
"++" {RULE("++"); return _INC; }
"--" {RULE("++"); return _DEC; }
/* Match boolean operators */
"==" return BOOLEQUAL;
"!=" return NOTEQUAL;
">=" return GREATEQUAL;
"<=" return LESSEQUAL;
">" return GREATER;
"<" return LESS;
and|AND|"&&" return _AND;
or|OR|"||" return _OR;
not|NOT|"!" return _NOT;
/* Start of a #define */
"#define"[ \t]+ { BEGIN (_DEF_MACRO); }
/* Start of the #define macro name */
<_DEF_MACRO>{_IDENTIFIER} {
RULE("FLEX: matched define macro: '#define %s'", scr_text);
/* Store macro name */
storeMacroName(scr_text);
BEGIN (_DEF_BODY_START);
}
<_DEF_BODY_START>[ \t]+ { BEGIN (_DEF_BODY); } /* Eat space between macro and body */
<_DEF_BODY>[^\r\n]* { /* Anything until the end of the line is macro body */
RULE("FLEX: matched define body: '#define %s %s' (%d)",
scr_macro[scr_num_macros].scr_define_macro, scr_text, scr_num_macros + 1);
/* Store macro body */
storeMacroBody(scr_text);
BEGIN (INITIAL);
}
/* Handle Includes */
"#include"[ \t]+\" { BEGIN (_INCL); } /* Match '#include' and skip whitespace until quotation */
<_INCL>[^ \t\n\"]+ { /* got the include file name */
RULE("FLEX: Opening include '%s' (scr_include_stack_ptr: %u)", scr_text, scr_include_stack_ptr);
/* Set up new input buffer, push current one to stack */
pushInclude(scr_text);
/* Match the ending quote */
BEGIN(_INCL_END_QUOTE);
}
<_INCL_END_QUOTE>\" {
int line;
char *text;
YY_BUFFER_STATE includeBuffer;
ASSERT(scr_include_stack_ptr < MAX_SCR_INCLUDE_DEPTH,
"FLEX: scr_include_stack_ptr out of bounds: %u", scr_include_stack_ptr);
ASSERT(pScrInputFiles[scr_include_stack_ptr] != NULL, "FLEX: failed to load include");
RULE("FLEX: storing line number for include %u: ",
scr_include_stack_ptr - 1, scr_lineno);
/* Store the current line number in the current input buffer */
scriptGetErrorData(&line, &text);
scrInclLine[scr_include_stack_ptr - 1] = line;
scr_lineno = 1; //we are at the beginning of the first line in a new input now
/* Create a new YY_BUFFER_STATE for this file */
includeBuffer = yy_create_buffer(NULL, YY_BUF_SIZE);
/* Switch to the new buffer */
yy_switch_to_buffer(includeBuffer);
BEGIN (INITIAL);
}
<<EOF>> {
RULE("FLEX: EOF: scr_include_stack_ptr: %u; scr_macro_stack_ptr: %d",
scr_include_stack_ptr, scr_macro_stack_ptr);
ASSERT(pScrInputFiles[scr_include_stack_ptr] != NULL, "FLEX: can't unload, buffer is NULL");
if ( scr_include_stack_ptr == 0 )
{
/* We finished scanning top level script or macro */
if(scr_macro_stack_ptr > 0)
{
RULE("FLEX: Finished parsing macro %d", scr_macro_stack_ptr);
popMacroStack();
}
else //both stacks are empty
{
RULE("FLEX: Finished parsing all input");
yyterminate();
}
}
else
{
/* First finish scanning all macros */
if(scr_macro_stack_ptr > 0)
{
RULE("FLEX: Finished parsing macro %d in include %d",
scr_macro_stack_ptr, scr_include_stack_ptr);
popMacroStack();
}
else //no macros, return to previous include
{
RULE("FLEX: returning from parsing include %u", scr_include_stack_ptr);
/* Free include buffer */
PHYSFS_close(pScrInputFiles[scr_include_stack_ptr]);
pScrInputFiles[scr_include_stack_ptr] = NULL;
/* Delete flex buffer */
yy_delete_buffer( YY_CURRENT_BUFFER );
/* Pop previous script file */
yy_switch_to_buffer( include_stack[scr_include_stack_ptr - 1] );
ASSERT(scr_include_stack_ptr != 0,
"Can't restore line number of theprevious flex input");
/* Restore line count of the previous buffer from the line count stack */
scr_lineno = scrInclLine[scr_include_stack_ptr - 1];
RULE("FLEX: restoring line number for include %u: ",
scr_include_stack_ptr, scr_lineno);
/* Go to the previous flex input buffer */
scr_include_stack_ptr--;
}
}
}
/* Match floating point numbers */
-?[0-9]*"."[0-9]+ {
RULE( "float matched '%s'", scr_text);
scr_lval.fval = (float)atof(scr_text);
return FLOAT_T;
}
/* Match integer numbers */
-?[0-9]+ { scr_lval.ival = atol(scr_text); return INTEGER; }
/* Match identifiers */
{_IDENTIFIER} {
char* tmpMacroBuffer;
RULE( "looking up '%s'", scr_text);
/* See if this identifier has been defined as a type */
if (scriptLookUpType(scr_text, &scr_lval.tval)) //STRUCTURESTAT etc
{
RULE( "'%s' is a user type", scr_text);
return TYPE;
}
/* See if this identifier has been defined as a variable */
else if (scriptLookUpVariable(scr_text, &scr_lval.vSymbol))
{
RULE( "'%s' is a var", scr_text);
return scriptGetVarToken(scr_lval.vSymbol);
}
/* See if this identifier has been defined as a constant */
else if (scriptLookUpConstant(scr_text, &scr_lval.cSymbol))
{
RULE( "'%s' is a constant", scr_text);
return scriptGetConstToken(scr_lval.cSymbol);
}
/* See if this identifier has been defined as a function */
else if (scriptLookUpFunction(scr_text, &scr_lval.fSymbol))
{
RULE( "'%s' is a function", scr_text);
return scriptGetFuncToken(scr_lval.fSymbol);
}
/* See if this identifier has been defined as a custom function */
else if (scriptLookUpCustomFunction(scr_text, &scr_lval.eSymbol))
{
RULE( "'%s' is a cust func", scr_text);
return scriptGetCustomFuncToken(scr_lval.eSymbol);
}
else if (scriptLookUpTrigger(scr_text, &scr_lval.tSymbol))
{
RULE( "'%s' is a trigger", scr_text);
return TRIG_SYM;
}
else if (scriptLookUpEvent(scr_text, &scr_lval.eSymbol))
{
RULE( "'%s' is an event", scr_text);
return EVENT_SYM;
}
else if (scriptLookUpCallback(scr_text, &scr_lval.cbSymbol))
{
RULE( "'%s' is a callback", scr_text);
return CALLBACK_SYM;
}
else if(scriptLoopUpMacro(scr_text, &tmpMacroBuffer))
{
RULE( "'%s' is a macro", scr_text);
ASSERT(tmpMacroBuffer != NULL,
"FLEX: failed to allocate memory for macro buffer %d", scr_macro_stack_ptr);
ASSERT(scr_macro_stack_ptr >= 0 && scr_macro_stack_ptr < MAX_SCR_MACRO_DEPTH,
"FLEX: flex macro buffer pointer out of bounds: %d", scr_macro_stack_ptr );
/* Push current buffer we are processing;
* can be either macro buffer, include buffer or top level script
*/
macro_stack[scr_macro_stack_ptr] = YY_CURRENT_BUFFER;
scr_macro_stack_ptr++;
pScrMacroBuffer[scr_macro_stack_ptr] = tmpMacroBuffer; // Assign to the new input buffer
/* Feed flex with the new macro buffer */
yy_switch_to_buffer( yy_scan_string( pScrMacroBuffer[scr_macro_stack_ptr] ) );
}
else
{
RULE( "'%s' is an ident", scr_text);
sstrcpy(aText[currText], scr_text);
scr_lval.sval = aText[currText];
currText = (currText + 1) % TEXT_BUFFERS;
return IDENT;
}
}
/* Strip macros */
"#" { BEGIN (_MACRO); }
<_MACRO>\n { BEGIN (INITIAL); }
<_MACRO>. ;
/* Match quoted text */
\" { BEGIN (QUOTE); }
<QUOTE>\" { BEGIN (INITIAL); }
<QUOTE>[^\"\n]* {
sstrcpy(aText[currText], scr_text);
scr_lval.sval = aText[currText];
currText = (currText + 1) % TEXT_BUFFERS;
//debug(LOG_SCRIPT, "%s is QTEXT", scr_text);
return QTEXT;
}
/* Skip white space */
[ \t\n\x0d\x0a] ;
/* Strip comments */
"/*" { inComment=true; BEGIN (COMMENT); }
<COMMENT>"*/" |
<COMMENT>"*/"\n { inComment=false; BEGIN (INITIAL); }
<COMMENT>. |
<COMMENT>\n ;
/* Strip single line comments */
"//" { BEGIN (SLCOMMENT); }
<SLCOMMENT>\n { BEGIN (INITIAL); }
<SLCOMMENT>[^\n]* ;
/* Match anything that's been missed and pass it as a char */
. return scr_text[0];
%%
/* Set the current input file for the lexer */
void scriptSetInputFile(PHYSFS_file* fileHandle)
{
YY_BUFFER_STATE inputScript;
unsigned int i;
/* Reset the lexer in case it's been used before */
scr__flush_buffer( YY_CURRENT_BUFFER );
/* Reset include stack */
scr_include_stack_ptr = 0;
/* Initialize include input files */
for(i = 0; i < MAX_SCR_INCLUDE_DEPTH; ++i)
{
pScrInputFiles[i] = NULL;
scrInclLine[i] = 0;
}
/* Set the initial input buffer */
pScrInputFiles[0] = fileHandle;
inputScript = yy_create_buffer(NULL, YY_BUF_SIZE);
yy_switch_to_buffer(inputScript);
/* Reset number of macros */
scr_num_macros = 0;
/* Reset macro stack */
scr_macro_stack_ptr = 0;
/* Initialize macro input buffers */
for(i = 0; i < MAX_SCR_MACRO_DEPTH; ++i)
{
pScrMacroBuffer[i] = NULL;
}
}
void scriptGetErrorData(int *pLine, char **ppText)
{
*pLine = scr_lineno;
*ppText = scr_text;
}
int scr_wrap(void)
{
if (inComment)
{
debug( LOG_ERROR, "Warning: reached end of file in a comment" );
abort();
}
return 1;
}
/* Older GNU Flex versions don't define yylex_destroy()
* (and neither define a subminor version)
*/
#if !defined(YY_FLEX_SUBMINOR_VERSION) || (YY_FLEX_SUBMINOR_VERSION < 9)
int scr_lex_destroy(void)
{
/* For non-reentrant C scanner only. */
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_init = 1;
return 0;
}
#endif