Parsing code.

This commit is contained in:
Andrei Drexler 2005-09-18 16:56:03 +00:00
parent c6e56cff05
commit 67d2fbfc30
4 changed files with 469 additions and 50 deletions

191
reaction/game/g_parser.c Normal file
View file

@ -0,0 +1,191 @@
#include "q_shared.h"
#include "g_parser.h"
char *Script_GetToken(char **str)
{
const char *whiteSpace = " ,;\n\r\t";
char *ret = NULL;
int inQuotes = 0;
if (!str || !*str || !**str)
return NULL;
//skip whitespace
while (**str && strchr(whiteSpace, **str) != NULL)
(*str)++;
//end of string?
if (!**str)
return NULL;
if ( inQuotes = (**str == '\"'))
(*str)++;
ret = *str;
//get actual token
for ( ; **str; (*str)++)
{
if (inQuotes)
{
if (**str == '\"')
break;
continue;
}
if (strchr(whiteSpace, **str) != NULL)
break;
}
//end token
if (**str)
*(*str)++ = 0;
return ret;
}
int Script_HashKey(const char *keyword)
{
int register hash, i;
hash = 0;
for (i = 0; keyword[i] != '\0'; i++) {
if (keyword[i] >= 'A' && keyword[i] <= 'Z')
hash += (keyword[i] + ('a' - 'A')) * (119 + i);
else
hash += keyword[i] * (119 + i);
}
hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (SCRIPT_HASH_SIZE - 1);
return hash;
}
void Script_InitHashTable(TParseState *state)
{
if (state->initialized)
return;
memset((*state).hashTable, 0, sizeof((*state).hashTable));
FOR_EACH_RULE(*state, rule)
{
int index = Script_HashKey(rule->keyword) % SCRIPT_HASH_SIZE;
TParseRule *next = (*state).hashTable[index];
rule->next = next;
(*state).hashTable[index] = rule;
}
END_FOR_RULE();
state->initialized = 1;
}
TParseFunc Script_FindHandler(char *token, TParseState* state)
{
TParseRule *ptr;
int index = Script_HashKey(token) % SCRIPT_HASH_SIZE;
if (!state->initialized)
Script_InitHashTable(state);
for (ptr = state->hashTable[index]; ptr; ptr = ptr->next)
{
if (stricmp(token, ptr->keyword) == 0)
return ptr->handler;
}
return NULL;
}
__inline void Script_InitStateStack(TStateStack *stack)
{
stack->top = 0;
}
void Script_ParseString(char *str, TParseState *baseState)
{
char *token = NULL;
TStateStack stateStack;
Script_InitStateStack(&stateStack);
Script_PushState(&stateStack, baseState);
while ( (token = Script_GetToken(&str)) != NULL)
{
TParseFunc handler = NULL;
TParseState *state;
Script_GetTopState(&stateStack, &state);
if ( (handler = Script_FindHandler(token, state)) != NULL)
{
handler(&str, &stateStack);
} else {
SCRIPT_INFO(SHOW_WARNING)
G_Printf("* Warning: unknown token, '%s'.\n", token);
END_SCRIPT_INFO();
}
}
}
int SCRIPT_SHOW_LEVEL = SHOW_NONE;
///////////////////////////////////////////////////////////////
// SAMPLE FUNCTIONS ///////////////////////////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
SCRIPT_FUNC(SFN_SkipComment)
///////////////////////////////////////////////////////////////
{
char *token;
while ( (token = Script_GetToken(SCRIPT_INPUT)) != NULL)
{
if (stricmp(token, "*/") == 0)
return 1;
}
SCRIPT_INFO(SHOW_ERROR)
G_Printf("* ERROR: end of input found in comment.\n");
END_SCRIPT_INFO();
return 0;
}
END_SCRIPT_FUNC()
///////////////////////////////////////////////////////////////
SCRIPT_FUNC(SFN_SkipLineComment)
///////////////////////////////////////////////////////////////
{
char *cursor;
for (cursor = *SCRIPT_INPUT; *cursor; cursor++)
{
if (*cursor == '\n' || *cursor == '\r')
{
*SCRIPT_INPUT = cursor + 1;
return 1;
}
}
SCRIPT_INFO(SHOW_ERROR)
G_Printf("* ERROR: end of input found in comment.\n");
END_SCRIPT_INFO();
return 0;
}
END_SCRIPT_FUNC()
///////////////////////////////////////////////////////////////
SCRIPT_FUNC(SFN_SkipToken)
///////////////////////////////////////////////////////////////
{
return 1;
}
END_SCRIPT_FUNC()
///////////////////////////////////////////////////////////////
SCRIPT_FUNC(SFN_PopState)
///////////////////////////////////////////////////////////////
{
SCRIPT_POP_STATE();
return 1;
}
END_SCRIPT_FUNC()

176
reaction/game/g_parser.h Normal file
View file

@ -0,0 +1,176 @@
#ifndef _G_PARSER_H_
#define _G_PARSER_H_
///////////////////////////////////////////////////////////////
#include "g_local.h"
#define STATE_STACK_SIZE 32
#define SCRIPT_HASH_SIZE 64
typedef struct
{
int top;
void *states[STATE_STACK_SIZE];
} TStateStack;
typedef int (*TParseFunc)(char **tokens, TStateStack *stack);
typedef struct _TParseRule
{
const char *keyword;
TParseFunc handler;
struct _TParseRule *next;
} TParseRule;
typedef struct
{
const char *name;
void *data;
int initialized;
TParseRule *hashTable[SCRIPT_HASH_SIZE];
TParseRule rules[];
} TParseState;
#define SCRIPT_STATE(name, data)\
TParseState name = {\
#name, /* state name */ \
data, /* optional data */ \
0, /* uninitialized */ \
{NULL}, /* hash table */
#define ADD_RULE(exp, func) {exp, &func, NULL},
#define END_STATE_MARKER {NULL, NULL, NULL}\
}
#define NO_DATA NULL
typedef enum
{
SHOW_ALL,
SHOW_WARNING,
SHOW_ERROR,
SHOW_NONE
} TErrorLevel;
///////////////////////////////////////////////////////////////
extern int SCRIPT_SHOW_LEVEL;
#define SCRIPT_INFO(level) if (SCRIPT_SHOW_LEVEL <= (level)) {
#define END_SCRIPT_INFO() }
#define NEEDS_TOKENS(num, name)\
char *name[num];\
{\
int TOKEN_INDEX;\
for (TOKEN_INDEX=0; TOKEN_INDEX<num; TOKEN_INDEX++)\
if ( (name[TOKEN_INDEX] = Script_GetToken(SCRIPT_INPUT)) == NULL)\
{\
SCRIPT_INFO(SHOW_ERROR)\
G_Printf("* ERROR: %s requires %d arguments instead of %d.\n", __SCRIPT_FUNC__, num, TOKEN_INDEX);\
END_SCRIPT_INFO();\
return 0;\
}\
}
#define SCRIPT_FUNC_PROTO(name) int name(char **SCRIPT_INPUT, TStateStack *SCRIPT_STACK)
#define SCRIPT_FUNC(name)\
SCRIPT_FUNC_PROTO(name)\
{\
const char *__SCRIPT_FUNC__ = #name;
#define END_SCRIPT_FUNC()\
}
#define FOR_EACH_RULE(state, rule)\
{\
int RULE_INDEX;\
TParseRule *rule;\
for (RULE_INDEX=0, rule = &(state).rules[0]; rule->keyword != NULL && rule->handler != NULL; RULE_INDEX++, rule++)
#define END_FOR_RULE() }
///////////////////////////////////////////////////////////////
__inline int Script_PushState(TStateStack *stack, TParseState *state)
{
if (stack->top >= STATE_STACK_SIZE)
{
SCRIPT_INFO(SHOW_ERROR)
G_Printf("* ERROR: Stack overflow before %s.\n", (state) ? state->name : "[UNKNOWN]");
END_SCRIPT_INFO();
return 0;
}
stack->states[stack->top++] = state;
return 1;
}
__inline int Script_GetTopState(const TStateStack *stack, TParseState **state)
{
if (!state)
return 1;
if (stack->top < 0 || stack->top >= STATE_STACK_SIZE)
{
*state = NULL;
SCRIPT_INFO(SHOW_ERROR)
G_Printf("* ERROR: Stack index out of bounds (%d).\n", stack->top);
END_SCRIPT_INFO();
return 0;
}
*state = stack->states[stack->top-1];
return 1;
}
__inline int Script_PopState(TStateStack *stack, TParseState **state)
{
stack->top--;
if (stack->top < 0)
{
SCRIPT_INFO(SHOW_ERROR)
G_Printf("* ERROR: Stack underflow before %s.\n", (stack->states[0]) ? ((TParseState*)stack->states[0])->name : "[UNKNOWN]");
END_SCRIPT_INFO();
if (state)
*state = NULL;
return 0;
}
if (state)
*state = stack->states[stack->top];
return 1;
}
char* Script_GetToken(char **str);
TParseFunc Script_FindHandler(char *token, TParseState *state);
void Script_ParseString(char *str, TParseState *baseState);
#define SCRIPT_PUSH_STATE(state) Script_PushState(SCRIPT_STACK, &state)
#define SCRIPT_POP_STATE() Script_PopState(SCRIPT_STACK, NULL)
#define SCRIPT_TRANSITION_TO(Dest) Transition_To_##Dest
#define SCRIPT_TRANSITION_FUNC(Dest)\
SCRIPT_FUNC(SCRIPT_TRANSITION_TO(Dest))\
{\
SCRIPT_PUSH_STATE(Dest);\
return 0;\
}\
END_SCRIPT_FUNC()
SCRIPT_FUNC_PROTO(SFN_SkipComment);
SCRIPT_FUNC_PROTO(SFN_SkipLineComment);
SCRIPT_FUNC_PROTO(SFN_SkipToken);
SCRIPT_FUNC_PROTO(SFN_PopState);
///////////////////////////////////////////////////////////////
#endif //_G_PARSER_H_

View file

@ -65,8 +65,8 @@ LINK32=link.exe
# PROP BASE Target_Dir "."
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "c:\reactionoutput"
# PROP Intermediate_Dir "c:\reactionoutput"
# PROP Output_Dir "d:\games\quake3\reaction"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "."
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
@ -351,6 +351,10 @@ SOURCE=.\g_matchmode.h
# End Source File
# Begin Source File
SOURCE=.\g_parser.h
# End Source File
# Begin Source File
SOURCE=.\g_public.h
# End Source File
# Begin Source File

View file

@ -6,61 +6,109 @@
--------------------Configuration: game - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
Creating temporary file "C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP15.tmp" with contents
Creating temporary file "C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP312.tmp" with contents
[
/nologo /G5 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "BUILDING_REF_GL" /D "DEBUG" /FR"c:\reactionoutput/" /Fp"c:\reactionoutput/game.pch" /YX /Fo"c:\reactionoutput/" /Fd"c:\reactionoutput/" /FD /c
"D:\Work\rq3source\reaction\game\g_teamplay.c"
/nologo /G5 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "BUILDING_REF_GL" /D "DEBUG" /FR"Debug/" /Fp"Debug/game.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /c
"D:\Work\rq3source\reaction\game\g_parser.c"
]
Creating command line "cl.exe @C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP15.tmp"
Creating temporary file "C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP16.tmp" with contents
Creating command line "cl.exe @C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP312.tmp"
Creating temporary file "C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP313.tmp" with contents
[
kernel32.lib user32.lib winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /incremental:yes /pdb:"c:\reactionoutput/qagamex86.pdb" /map:"c:\reactionoutput/qagamex86.map" /debug /machine:I386 /def:".\game.def" /out:"D:\Work\rq3source\reaction\Release\qagamex86.dll" /implib:"c:\reactionoutput/qagamex86.lib"
c:\reactionoutput\ai_chat.obj
c:\reactionoutput\ai_cmd.obj
c:\reactionoutput\ai_dmnet.obj
c:\reactionoutput\ai_dmq3.obj
c:\reactionoutput\ai_main.obj
c:\reactionoutput\ai_team.obj
c:\reactionoutput\ai_vcmd.obj
c:\reactionoutput\bg_misc.obj
c:\reactionoutput\bg_pmove.obj
c:\reactionoutput\bg_slidemove.obj
c:\reactionoutput\g_active.obj
c:\reactionoutput\g_arenas.obj
c:\reactionoutput\g_bot.obj
c:\reactionoutput\g_client.obj
c:\reactionoutput\g_cmds.obj
c:\reactionoutput\g_combat.obj
c:\reactionoutput\g_fileio.obj
c:\reactionoutput\g_items.obj
c:\reactionoutput\g_main.obj
c:\reactionoutput\g_matchmode.obj
c:\reactionoutput\g_mem.obj
c:\reactionoutput\g_misc.obj
c:\reactionoutput\g_missile.obj
c:\reactionoutput\g_mover.obj
c:\reactionoutput\g_session.obj
c:\reactionoutput\g_spawn.obj
c:\reactionoutput\g_svcmds.obj
c:\reactionoutput\g_syscalls.obj
c:\reactionoutput\g_target.obj
c:\reactionoutput\g_team.obj
c:\reactionoutput\g_teamplay.obj
c:\reactionoutput\g_trigger.obj
c:\reactionoutput\g_unlagged.obj
c:\reactionoutput\g_utils.obj
c:\reactionoutput\g_weapon.obj
c:\reactionoutput\q_math.obj
c:\reactionoutput\q_shared.obj
c:\reactionoutput\rxn_game.obj
c:\reactionoutput\zcam.obj
c:\reactionoutput\zcam_target.obj
kernel32.lib user32.lib winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /incremental:yes /pdb:"d:\games\quake3\reaction/qagamex86.pdb" /map:"Debug/qagamex86.map" /debug /machine:I386 /def:".\game.def" /out:"D:\Work\rq3source\reaction\Release\qagamex86.dll" /implib:"d:\games\quake3\reaction/qagamex86.lib"
.\Debug\ai_chat.obj
.\Debug\ai_cmd.obj
.\Debug\ai_dmnet.obj
.\Debug\ai_dmq3.obj
.\Debug\ai_main.obj
.\Debug\ai_team.obj
.\Debug\ai_vcmd.obj
.\Debug\bg_misc.obj
.\Debug\bg_pmove.obj
.\Debug\bg_slidemove.obj
.\Debug\g_active.obj
.\Debug\g_arenas.obj
.\Debug\g_bot.obj
.\Debug\g_client.obj
.\Debug\g_cmds.obj
.\Debug\g_combat.obj
.\Debug\g_fileio.obj
.\Debug\g_items.obj
.\Debug\g_main.obj
.\Debug\g_matchmode.obj
.\Debug\g_mem.obj
.\Debug\g_misc.obj
.\Debug\g_missile.obj
.\Debug\g_mover.obj
.\Debug\g_session.obj
.\Debug\g_spawn.obj
.\Debug\g_svcmds.obj
.\Debug\g_syscalls.obj
.\Debug\g_target.obj
.\Debug\g_team.obj
.\Debug\g_teamplay.obj
.\Debug\g_trigger.obj
.\Debug\g_unlagged.obj
.\Debug\g_utils.obj
.\Debug\g_weapon.obj
.\Debug\q_math.obj
.\Debug\q_shared.obj
.\Debug\rxn_game.obj
.\Debug\zcam.obj
.\Debug\zcam_target.obj
.\Debug\g_parser.obj
]
Creating command line "link.exe @C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP16.tmp"
Creating command line "link.exe @C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP313.tmp"
<h3>Output Window</h3>
Compiling...
g_teamplay.c
g_parser.c
Linking...
Creating temporary file "C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP314.tmp" with contents
[
/nologo /o"d:\games\quake3\reaction/game.bsc"
.\Debug\ai_chat.sbr
.\Debug\ai_cmd.sbr
.\Debug\ai_dmnet.sbr
.\Debug\ai_dmq3.sbr
.\Debug\ai_main.sbr
.\Debug\ai_team.sbr
.\Debug\ai_vcmd.sbr
.\Debug\bg_misc.sbr
.\Debug\bg_pmove.sbr
.\Debug\bg_slidemove.sbr
.\Debug\g_active.sbr
.\Debug\g_arenas.sbr
.\Debug\g_bot.sbr
.\Debug\g_client.sbr
.\Debug\g_cmds.sbr
.\Debug\g_combat.sbr
.\Debug\g_fileio.sbr
.\Debug\g_items.sbr
.\Debug\g_main.sbr
.\Debug\g_matchmode.sbr
.\Debug\g_mem.sbr
.\Debug\g_misc.sbr
.\Debug\g_missile.sbr
.\Debug\g_mover.sbr
.\Debug\g_session.sbr
.\Debug\g_spawn.sbr
.\Debug\g_svcmds.sbr
.\Debug\g_syscalls.sbr
.\Debug\g_target.sbr
.\Debug\g_team.sbr
.\Debug\g_teamplay.sbr
.\Debug\g_trigger.sbr
.\Debug\g_unlagged.sbr
.\Debug\g_utils.sbr
.\Debug\g_weapon.sbr
.\Debug\q_math.sbr
.\Debug\q_shared.sbr
.\Debug\rxn_game.sbr
.\Debug\zcam.sbr
.\Debug\zcam_target.sbr
.\Debug\g_parser.sbr]
Creating command line "bscmake.exe @C:\DOCUME~1\Andrei\LOCALS~1\Temp\RSP314.tmp"
Creating browse info file...
<h3>Output Window</h3>