warzone2100/lib/script/chat_parser.y

770 lines
20 KiB
Plaintext

/*
This file is part of Warzone 2100.
Copyright (C) 2006-2008 Roman
Copyright (C) 2006-2010 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
*/
%{
/*
* chat_parser.y
*
* yacc grammar for multiplayer chat messages
*
*/
#include "lib/framework/frame.h"
#include "lib/framework/string_ext.h"
#include "lib/framework/frameresource.h"
#include "lib/script/chat_processing.h"
#define MAX_CHAT_ARGUMENTS 10
/* Holds information about a processed player chat message */
CHAT_MSG chat_msg;
// Parameter variable
static INTERP_VAL scrParameter;
extern int chat_lex(void);
// Store extracted command for use in scripts
static void chat_store_command(const char *command);
/* Return value of the chat parsing - to be used in scripts */
static BOOL chat_store_parameter(INTERP_VAL *parameter);
// Store players that were addressed in a command
static void chat_store_player(SDWORD cmdIndex, SDWORD playerIndex);
// Reset a command
static void chat_reset_command(SDWORD cmdIndex);
// Information extracted from message and available for scripts
//static int numMsgParams=0; //number of parameters currently extracted/stored
//static INTERP_VAL msgParams[MAX_CHAT_ARGUMENTS];
/* Store command parameter extracted from the message */
static BOOL chat_store_parameter(INTERP_VAL *cmdParam)
{
SDWORD numCmdParams, numCommands;
//console("chat_store_parameter: new parameter");
/* Make sure we have no overflow */
//if(numMsgParams >= MAX_CHAT_ARGUMENTS)
if(chat_msg.numCommands >= MAX_CHAT_COMMANDS)
{
ASSERT(false, "chat_store_parameter: too many commands in a message");
return false;
}
numCommands = chat_msg.numCommands;
numCmdParams = chat_msg.cmdData[numCommands].numCmdParams;
/* Make sure we still have room for more parameters */
if(numCmdParams >= MAX_CHAT_CMD_PARAMS)
{
ASSERT(false, "chat_store_parameter: out of parameters for command %d", numCommands);
return false;
}
/* Store parameter for command we are currently processing */
//memcpy(&(msgParams[numMsgParams]), parameter, sizeof(INTERP_VAL));
memcpy(&(chat_msg.cmdData[numCommands].parameter[numCmdParams]), cmdParam, sizeof(INTERP_VAL));
chat_msg.cmdData[numCommands].numCmdParams++;
return true;
}
// Store extracted command for use in scripts
static void chat_store_command(const char *command)
{
SDWORD numCmdParams, numCommands;
//console("chat_store_command: new command: %s", command);
/* Make sure we have no overflow */
if(chat_msg.numCommands >= MAX_CHAT_COMMANDS)
{
ASSERT(false, "chat_store_command: too many commands in a message");
return;
}
numCommands = chat_msg.numCommands;
numCmdParams = chat_msg.cmdData[numCommands].numCmdParams;
/* Make sure we still have room for more parameters */
if(numCmdParams >= MAX_CHAT_CMD_PARAMS)
{
ASSERT(false, "chat_store_command: out of parameters for command %d", numCommands);
return;
}
/* Store command */
chat_msg.cmdData[numCommands].pCmdDescription = command;
chat_msg.numCommands++;
}
// Store players that were addressed in a command
static void chat_store_player(SDWORD cmdIndex, SDWORD playerIndex)
{
SDWORD i;
//console("chat_store_player: player %d addressd in command %d", playerIndex, cmdIndex);
/* Make sure we have no overflow */
if(cmdIndex < 0 || cmdIndex >= MAX_CHAT_COMMANDS)
{
ASSERT(false, "chat_store_player: command message out of bounds: %d", cmdIndex);
return;
}
if(playerIndex == -1) //no player specified means all players addressed
{
/* Ally players addressed */
for(i=0; i<MAX_PLAYERS; i++)
{
chat_msg.cmdData[cmdIndex].bPlayerAddressed[i] = true;
}
}
else if(playerIndex >= 0 && playerIndex < MAX_PLAYERS)
{
chat_msg.cmdData[cmdIndex].bPlayerAddressed[playerIndex] = true;
}
else /* Wrong player index */
{
ASSERT(false, "chat_store_player: wrong player index: %d", playerIndex);
return;
}
}
static void chat_reset_command(SDWORD cmdIndex)
{
SDWORD i;
ASSERT(cmdIndex >= 0 && cmdIndex < MAX_CHAT_COMMANDS,
"chat_reset_command: command index out of bounds: %d", cmdIndex);
chat_msg.cmdData[cmdIndex].numCmdParams = 0;
for(i=0; i<MAX_PLAYERS; i++)
{
chat_msg.cmdData[cmdIndex].bPlayerAddressed[i] = false;
}
}
static void yyerror(const char* msg);
%}
%name-prefix="chat_"
%union {
BOOL bval;
SDWORD ival;
}
/* Start symbol */
%start R_PHRASE
/* keywords */
%token BRACKET_OPEN
%token BRACKET_CLOSE
%token SQ_BRACKET_OPEN
%token SQ_BRACKET_CLOSE
%token PIPE
%token T_WORD
%token R_POSSESSION
%token _T_QM
%token _T_EM
%token _T_FULLSTOP
%token _T_COLON
%token _T_SEMICOLON
%token _T_COMMA
%token _T_A
%token _T_AFFIRMATIVE
%token _T_AFTER
%token _T_ALLY
%token _T_AM
%token _T_AN
%token _T_AND
%token _T_ANY
%token _T_ATTACK
%token _T_ATTACKING
%token _T_ARMY
%token _T_BEACON
%token _T_BUILDING
%token _T_CANT
%token _T_CENTER
%token _T_DEAD
%token _T_DERRICK
%token _T_DO
%token _T_DROP
%token _T_FINE
%token _T_FOR
%token _T_FORCE
%token _T_GET
%token _T_GETTING
%token _T_GO
%token _T_GOING
%token _T_GONNA
%token _T_GOT
%token _T_GREAT
%token _T_HAVE
%token _T_HAS
%token _T_HELP
%token _T_HOLD
%token _T_I
%token _T_IM
%token _T_IS
%token _T_LASSAT
%token _T_LETS
%token _T_ME
%token _T_MORE
%token _T_NEED
%token _T_NO
%token _T_NOW
%token _T_OFCOURSE
%token _T_OK
%token _T_ON
%token _T_PLACE
%token _T_POSSESSION
%token _T_POWER
%token _T_PUMPING
%token _T_PUT
%token _T_READY
%token _T_REQUIRE
%token _T_ROGER
%token _T_RUSH
%token _T_SEC
%token _T_SEE
%token _T_SOME
%token _T_STATUS
%token _T_STOP
%token _T_SURE
%token _T_THANK_YOU
%token _T_THANKS
%token _T_THE
%token _T_U
%token _T_UNITS
%token _T_VTOLS
%token _T_WAIT
%token _T_WHERE
%token _T_YES
%token _T_YOU
%token _T_EOF 0
/* Typed Keywords */
%token <ival> R_PLAYER
%token <ival> R_INTEGER
/* Rules */
%type <ival> R_PLAYER_POSSESSION
%%
/* The top-level rule, consists of
* one or more sentences: commands or addressed commands
* or unrecognized messages (handled in R_COMMAND)
*/
R_PHRASE: R_ADDRESSED_COMMAND
| R_COMMAND
{
chat_store_player(chat_msg.numCommands - 1, -1); /* No player list means current command is addressed to everyone */
}
| R_PHRASE R_ADDRESSED_COMMAND
| R_PHRASE R_COMMAND
{
chat_store_player(chat_msg.numCommands - 1, -1);
}
| error
{
//console("-unrecognized phrase");
/* Reset current command */
chat_reset_command(chat_msg.numCommands);
}
;
/* A command addressed to one or more players
*/
R_ADDRESSED_COMMAND: R_PLAYER_LIST _T_COLON R_COMMAND /* "Red, Black: attack yellow" */
//{console("RULE: player list: command");}
| R_PLAYER_LIST _T_SEMICOLON R_COMMAND /* "Red, Black; attack yellow" */
//{console("RULE: player list; command");}
| R_PLAYER_LIST _T_COMMA R_COMMAND /* "Red, Black, attack yellow" */
//{console("RULE: player list, command");}
| R_PLAYER_LIST R_COMMAND /* No delimeter: "Red, Black attack yellow" */
//{console("RULE: player list command");}
;
/* Any of the recognizable commands -
* since there can't be any player list at this stage,
* should address all players receiving this message by default:
* EXAMPLE:
* 1) "Help me!" - addresses all players receiving the message
* 2) "Black and Red, help me!" - addresses players 'Black' and 'red' only
*/
R_COMMAND: R_ALLY_OFFER /* ally me */
{
chat_store_command("ally me"); /* Store current command */
}
| R_ASK_READINESS /* go? */
{
chat_store_command("go?");
}
| R_INITIATE_ACTION /* go ! */
{
chat_store_command("go!");
}
| R_DEMAND_BEACON /* drop a beacon */
{
chat_store_command("drop a beacon");
}
| R_MEET_CENTER /* go center */
{
chat_store_command("go center");
}
| R_ASK_STATUS /* status? */
{
chat_store_command("status?");
}
| R_BUILDING_UNITS /* pumping units */
{
chat_store_command("pumping units");
}
| R_STOP /* stop! */
{
chat_store_command("stop");
}
| R_WONDER_IF_HAVE_POWER /* Ask if player has power */
{
chat_store_command("got power?");
}
| R_DEMAND_HELP /* help me! */
{
chat_store_command("help me");
}
| R_REPORT_SAFETY /* i'm ok */
{
chat_store_command("i'm ok");
}
| R_AFFIRMATIVE /* roger! */
{
chat_store_command("roger");
}
| R_ALLY_PLAYER /* ally blue */
{
chat_store_command("ally player");
}
| R_ATTACK_PLAYER /* attack blue */
{
chat_store_command("attack player");
}
| R_ATTACKING_PLAYER /* attacking blue */
{
chat_store_command("attacking player?");
}
| R_PLAYER_HAS_VTOLS /* blue has VTOLS */
{
chat_store_command("player has vtols");
}
| R_GETTING_ENEMY_DERRICK /* gonna get blue's derrick */
{
chat_store_command("getting player oil");
}
| R_LASSAT_PLAYER /* lassat red */
{
chat_store_command("lassat player");
}
| R_WAIT_FOR_ME /* wait for me */
{
chat_store_command("wait for me");
}
| R_RUSH_INITIATION /* rush? */
{
chat_store_command("rush");
}
;
/* A reference to certain players: "yellow, help me"
*/
R_PLAYER_LIST: R_PLAYER
{
chat_store_player(chat_msg.numCommands, $1); /* Remember this player was addressed in current command */
}
| R_PLAYER_LIST R_PLAYER /* without any delimeters: "yellow black red" */
{
chat_store_player(chat_msg.numCommands, $2);
}
| R_PLAYER_LIST _T_COMMA R_PLAYER /* "yellow, black, red" */
{
chat_store_player(chat_msg.numCommands, $3);
}
| R_PLAYER_LIST _T_AND R_PLAYER /* "yellow, black and red" */
{
chat_store_player(chat_msg.numCommands, $3);
}
;
R_PLAYER_POSSESSION: R_PLAYER R_POSSESSION /* blue's */
{
$$ = $1; // just pass player index
}
;
/* A Article or nothing
*/
R_A_OR_EMPTY: /* Empty */
| _T_A
| _T_AN
| _T_THE
;
/* Punctuation mark */
R_PUNCTUATION_MARK: _T_QM
| _T_EM
| R_PUNCTUATION_MARK _T_QM /* ?????!!!!??! */
| R_PUNCTUATION_MARK _T_EM /* ?????!!!!??! */
;
/* Full stop or end of file
* (a 'virtual' rule)
*/
R_FULLSTOP_OR_EOF: /* Empty */ /* Needed for other rules */
| _T_EOF /* End of input */
| _T_FULLSTOP /* . */
;
/* End of a declarative sentence
* can't be a question
*/
R_EOD: _T_EM /* !!!! */
| R_FULLSTOP_OR_EOF
;
/* End of a question
* can't end with an exclamation mark
*/
R_EOQ: _T_QM
| R_FULLSTOP_OR_EOF
;
/* (any possible) end of the sentence - similar
* to "End of a declarative sentence",
* but can additionally end with a question.
*/
R_EOS: R_EOD /* End of a declarative sentence */
| R_PUNCTUATION_MARK /* ???!!?!?!?!? */
;
/* Express attack intention */
R_ATTACKING: _T_ATTACKING
| _T_GOING
| _T_GOING _T_AFTER
;
/* Attack */
R_INITIATE_ATTACK: _T_ATTACK
| _T_GET
;
R_PUT_DOWN: _T_PUT | _T_DROP | _T_PLACE; /* put */
R_CONSTRUCTING: _T_PUMPING /* pumping/getting */
| _T_GETTING
| _T_BUILDING
;
R_YOU: _T_YOU
| _T_U /* u */
;
/* 'You' - pronoun for questions */
R_DO_YOU: R_YOU /* like in "you got any..." */
| _T_DO R_YOU /* do you */
;
/* Used in questions */
R_POSSESSION_Q: _T_HAVE
| _T_GOT
;
R_POSSESSES: _T_HAS
| _T_GOT
;
R_QUANTITY: _T_ANY
| _T_SOME
;
R_DO_YOU_HAVE_ANY: R_DO_YOU R_POSSESSION_Q R_QUANTITY
| R_DO_YOU R_POSSESSION_Q
| R_POSSESSION_Q R_QUANTITY /* got any.. */
| R_POSSESSION_Q /* got <substantive>? */
| R_QUANTITY /* any <substantive>? */
;
R_CONFIDENCE_EXPRESSION: _T_SURE
| _T_OFCOURSE
;
R_AGREEMENT_EXPRESSION: _T_YES
| _T_FINE
| _T_OK
;
R_AFFIRMATIVE_FORMS: R_AGREEMENT_EXPRESSION
| R_CONFIDENCE_EXPRESSION
| _T_ROGER /* roger */
| _T_AFFIRMATIVE
;
/* Need */
R_NEED: _T_NEED
| _T_REQUIRE
;
R_RUSH_INITIATION: _T_RUSH R_EOS /* rush? */
| _T_LETS _T_RUSH R_EOS /* let's rush */
;
/*******************************************/
/* FINAL RULES, SHOULD BE PART OF R_PHRASE */
/*******************************************/
/* Ask a player to ally myself */
R_ALLY_OFFER: _T_ALLY _T_ME R_EOS /* ally me */
| _T_ALLY R_EOS /* "ally" at the end (otherwise breaks "ally me") */
/* | _T_ALLY */
;
/* Chech if player is willing
* to initiate some actions
*/
R_ASK_READINESS: _T_GO _T_QM /* go? */
| _T_READY R_EOS /* "ready!" or "ready?" */
| _T_IM _T_READY R_EOS /* I'm ready! */
;
/* Tell to start some action */
R_INITIATE_ACTION: _T_GO R_EOD; /* go!! */
/* Tell to drop a beacon */
R_DEMAND_BEACON: R_PUT_DOWN R_A_OR_EMPTY _T_BEACON; /* put a beacon */
/* Tell to meet in the center of the map */
R_MEET_CENTER: _T_GO _T_CENTER R_EOS; /* go center */
/* Ask for the current status */
R_ASK_STATUS: _T_STATUS R_EOS; /* status? */
/* Units */
R_UNIT_ARMY: R_A_OR_EMPTY _T_FORCE
| R_A_OR_EMPTY _T_ARMY
;
/* Player is building units */
R_BUILDING_UNITS: R_CONSTRUCTING _T_UNITS R_EOD /* pumping units */
| R_CONSTRUCTING _T_MORE _T_UNITS R_EOD /* pumping more units */
| R_CONSTRUCTING R_UNIT_ARMY R_EOD /* building army */
;
/* Stop command */
R_STOP: _T_STOP R_EOD; /* stop */
/* Ask if player has power */
R_WONDER_IF_HAVE_POWER: R_DO_YOU_HAVE_ANY _T_POWER R_EOQ; /* do you have power? */
/* Ask for help */
R_DEMAND_HELP: _T_HELP _T_ME R_EOS /* help me!!!!! */
| _T_HELP R_EOS /* help!?!? */
| _T_I R_DEMAND_HELP /* I need help */
| R_NEED _T_HELP R_EOS /* need help */
| R_NEED R_QUANTITY _T_HELP R_EOS /* need some help */
;
R_GRATITUDE: _T_THANK_YOU
| _T_THANKS
;
/* Tell player i'm safe - no danger anymore */
R_REPORT_SAFETY: _T_IM _T_OK R_EOD /* i'm ok */
| _T_IM _T_FINE R_EOD
| R_REPORT_SAFETY _T_NOW R_EOD
| R_REPORT_SAFETY R_GRATITUDE R_EOD
| R_REPORT_SAFETY _T_COMMA R_GRATITUDE R_EOD
| R_GRATITUDE R_REPORT_SAFETY R_EOD
| R_GRATITUDE _T_COMMA R_REPORT_SAFETY R_EOD
| R_GRATITUDE R_GRATITUDE R_EOD
;
/* Positive order feedback */
R_AFFIRMATIVE: R_AFFIRMATIVE_FORMS R_EOD /* yes, roger etc */
| R_AFFIRMATIVE R_AFFIRMATIVE_FORMS R_EOD /* accept repetitions: "yes yes yeah" */
// | R_AFFIRMATIVE /* can also be emotional: "yea!!! yeah! yes!!!!" */
;
/* Ask to ally a player */
R_ALLY_PLAYER: _T_ALLY R_PLAYER R_EOD /* ally blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
yyerror("chat command: Too many parameters in the message");
}
}
;
/* Ask to start attack a player */
R_ATTACK_PLAYER: R_INITIATE_ATTACK R_PLAYER R_EOD /* attack blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
yyerror("chat command: Too many parameters in the message");
}
}
| _T_GO R_PLAYER R_EOD /* go blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
yyerror("chat command: Too many parameters in the message");
}
}
;
/* Notify i'm attacking player */
R_ATTACKING_PLAYER: R_ATTACKING R_PLAYER R_EOD /* attacking blue */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
yyerror("chat command: Too many parameters in the message");
}
}
;
/* Notify about player using VTOLs */
R_PLAYER_HAS_VTOLS: R_PLAYER R_POSSESSES _T_VTOLS R_EOD /* blue has VTOLS */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $1;
if(!chat_store_parameter(&scrParameter))
{
yyerror("chat command: Too many parameters in the message");
}
}
;
R_GETTING_ENEMY_DERRICK: _T_GONNA R_INITIATE_ATTACK R_PLAYER_POSSESSION _T_DERRICK R_EOD /* gonna get blue's derrick */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $3;
if(!chat_store_parameter(&scrParameter))
{
yyerror("chat command: Too many parameters in the message");
}
}
;
R_LASSAT_PLAYER: _T_LASSAT R_PLAYER R_EOD /* gonna get blue's derrick */
{
/* Store for scripts */
scrParameter.type = VAL_INT;
scrParameter.v.ival = $2;
if(!chat_store_parameter(&scrParameter))
{
yyerror("chat command: Too many parameters in the message");
}
}
;
R_WAIT_FOR_ME: _T_WAIT R_EOD /* wait */
| _T_SEC R_EOD /* sec... */
| _T_WAIT _T_FOR _T_ME R_EOD /* wait for me */
| _T_HOLD _T_ON R_EOD /* hold on */
;
%%
/* Initialize Bison and start chat processing */
BOOL chatLoad(char *pData, UDWORD size)
{
SDWORD cmdIndex,parseResult;
/* Don't parse the same message again for a different player */
if(strcmp(pData, &(chat_msg.lastMessage[0])) == 0) //just parsed this message for some other player
{
return true; //keep all the parsed data unmodified
}
/* Tell bison what to parse */
chatSetInputBuffer(pData, size);
/* Reset previous chat processing */
chat_msg.numCommands = 0;
/* Initialize command data */
for(cmdIndex=0; cmdIndex<MAX_CHAT_COMMANDS; cmdIndex++)
{
chat_reset_command(cmdIndex);
}
/* Invoke bison and parse */
parseResult = chat_parse();
/* Remember last message we parsed */
sstrcpy(chat_msg.lastMessage, pData);
/* See if we were successful parsing */
if (parseResult != 0)
{
return false;
}
return true;
}
/* A simple error reporting routine */
static void yyerror(const char* msg)
{
int line;
char *pText;
chatGetErrorData(&line, &pText);
//debug(LOG_WARNING, "multiplayer message parse error: %s at line %d, token: %d, text: '%s'",
// msg, line, chat_char, pText);
}