mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-17 22:50:51 +00:00
Cleaned up Cmd_Args. It is no longer uselessly built up in
Cmd_TokenizeString since I realized the code was just pulling it apart and putting it back together. Added Cmd_Argsu to access the unprocessed command. Added support for else following if in such a way that if/else if/else structures work. Added support for unary operators in EXP and added the ! (not) operator. The ifnot command will be kept for variety.
This commit is contained in:
parent
e4975cd675
commit
bf323c3f0a
6 changed files with 115 additions and 53 deletions
|
@ -58,6 +58,7 @@ typedef struct cmd_buffer_s {
|
|||
struct dstring_s *looptext; // If a looping buffer, the text we are looping on
|
||||
struct dstring_s *retval; // Return value
|
||||
unsigned int *args; // Array of positions of each token in composite line
|
||||
unsigned int *argsu; // Array of positions of each token in original line
|
||||
struct hashtab_s *locals; // Local variables
|
||||
|
||||
// Flags
|
||||
|
|
|
@ -59,6 +59,7 @@ typedef struct optable_s
|
|||
{
|
||||
char *str;
|
||||
opfunc func;
|
||||
unsigned int operands;
|
||||
} optable_t;
|
||||
|
||||
extern exp_error_t EXP_ERROR;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#ifndef __ops_h
|
||||
#define __ops_h
|
||||
|
||||
float OP_Not (float op1, float op2);
|
||||
float OP_Add (float op1, float op2);
|
||||
float OP_Sub (float op1, float op2);
|
||||
float OP_Mult (float op1, float op2);
|
||||
|
|
|
@ -854,6 +854,13 @@ Cmd_Args (int start)
|
|||
return cmd_activebuffer->line->str + cmd_activebuffer->args[start];
|
||||
}
|
||||
|
||||
const char *
|
||||
Cmd_Argsu (int start)
|
||||
{
|
||||
if (start >= cmd_activebuffer->argc)
|
||||
return "";
|
||||
return cmd_activebuffer->realline->str + cmd_activebuffer->argsu[start];
|
||||
}
|
||||
|
||||
int
|
||||
Cmd_EndDoubleQuote (const char *str)
|
||||
|
@ -1371,7 +1378,7 @@ Cmd_Process (void)
|
|||
cmd_activebuffer->args[arg] += adj;
|
||||
adj += (str->size - 1) - (org->size - 1);
|
||||
dstring_replace (cmd_activebuffer->line, str->str, str->size - 1,
|
||||
cmd_activebuffer->args[arg] + quotes, org->size - 1);
|
||||
cmd_activebuffer->args[arg] + quotes + (cmd_activebuffer->argv[arg]->delim == '{'), org->size - 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1387,6 +1394,8 @@ Cmd_TokenizeString (const char *text, qboolean legacy)
|
|||
|
||||
dstring_clearstr (cmd_activebuffer->realline);
|
||||
dstring_appendstr (cmd_activebuffer->realline, text);
|
||||
dstring_clearstr (cmd_activebuffer->line);
|
||||
dstring_appendstr (cmd_activebuffer->line, text);
|
||||
|
||||
/* Turn off tags at the beginning of a command. This causes tags to
|
||||
continue past token boundaries. */
|
||||
|
@ -1394,7 +1403,6 @@ Cmd_TokenizeString (const char *text, qboolean legacy)
|
|||
tag_special = 0;
|
||||
if (text[0] == '|')
|
||||
str++;
|
||||
dstring_clearstr (cmd_activebuffer->line);
|
||||
while (strlen (str + i)) {
|
||||
if (!legacy && cmd_argc == 1) { // See if command wants unprocessed tokens
|
||||
cmd = (cmd_function_t *) Hash_Find (cmd_hash, cmd_activebuffer->argv[0]->original->str);
|
||||
|
@ -1406,8 +1414,6 @@ Cmd_TokenizeString (const char *text, qboolean legacy)
|
|||
i++;
|
||||
space++;
|
||||
}
|
||||
if (space)
|
||||
dstring_appendsubstr (cmd_activebuffer->line, str + i - space, space);
|
||||
len = Cmd_GetToken (str + i, legacy);
|
||||
if (len < 0) {
|
||||
Cmd_Error ("Parse error: Unmatched quotes, braces, or "
|
||||
|
@ -1421,6 +1427,9 @@ Cmd_TokenizeString (const char *text, qboolean legacy)
|
|||
cmd_activebuffer->argv = realloc (cmd_activebuffer->argv,
|
||||
sizeof (cmd_token_t *) * cmd_argc);
|
||||
SYS_CHECKMEM (cmd_activebuffer->argv);
|
||||
cmd_activebuffer->argsu = realloc (cmd_activebuffer->argsu,
|
||||
sizeof (int) * cmd_argc);
|
||||
SYS_CHECKMEM (cmd_activebuffer->argsu);
|
||||
|
||||
cmd_activebuffer->args = realloc (cmd_activebuffer->args,
|
||||
sizeof (int) * cmd_argc);
|
||||
|
@ -1434,6 +1443,7 @@ Cmd_TokenizeString (const char *text, qboolean legacy)
|
|||
/* Remove surrounding quotes or double quotes or braces */
|
||||
quotes = 0;
|
||||
braces = 0;
|
||||
cmd_activebuffer->argsu[cmd_argc - 1] = i;
|
||||
cmd_activebuffer->args[cmd_argc - 1] = i;
|
||||
cmd_activebuffer->argv[cmd_argc - 1]->delim = ' ';
|
||||
if ((!legacy && str[i] == '\'' && str[i + len] == '\'')
|
||||
|
@ -1443,8 +1453,6 @@ Cmd_TokenizeString (const char *text, qboolean legacy)
|
|||
len -= 1;
|
||||
quotes = 1;
|
||||
}
|
||||
dstring_appendsubstr (cmd_activebuffer->line,
|
||||
str + i - quotes, len + quotes * 2);
|
||||
if (str[i] == '{' && str[i + len] == '}') {
|
||||
i++;
|
||||
len -= 1;
|
||||
|
@ -1893,11 +1901,21 @@ Cmd_If_f (void)
|
|||
{
|
||||
long int num;
|
||||
|
||||
if (Cmd_Argc () < 3) {
|
||||
Sys_Printf ("Usage: if {condition} {commands}\n");
|
||||
if ((Cmd_Argc () !=3 && !(Cmd_Argc () >= 5)) || (Cmd_Argc () > 5 && strcmp(Cmd_Argv(3),"else"))) {
|
||||
Sys_Printf ("Usage: if {condition} {commands} else {commands}\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* HACK HACK HACK
|
||||
If is set as a pure command, but it needs the first argument
|
||||
to be evaluated. Normally this would mean Cmd_Args is out
|
||||
of sync, but since if uses Cmd_Argsu (4) and no other commands
|
||||
will need these tokens, it is safe.
|
||||
*/
|
||||
if (Cmd_ProcessToken (cmd_activebuffer->argv[1]) < 0)
|
||||
return;
|
||||
cmd_activebuffer->argv[1]->state = cmd_done;
|
||||
|
||||
num = strtol (Cmd_Argv(1), 0, 10);
|
||||
|
||||
if (!strcmp(Cmd_Argv(0), "ifnot"))
|
||||
|
@ -1905,6 +1923,11 @@ Cmd_If_f (void)
|
|||
|
||||
if (num)
|
||||
Cbuf_InsertText (Cmd_Argv (2));
|
||||
if (!num && Cmd_Argc() == 5)
|
||||
Cbuf_InsertText (Cmd_Argv (4));
|
||||
if (!num && Cmd_Argc() > 5) {
|
||||
Cbuf_InsertText (Cmd_Argsu (4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2116,6 +2139,7 @@ Cmd_Init (void)
|
|||
"variable");
|
||||
|
||||
Cmd_AddCommand ("if", Cmd_If_f, "Conditionally execute a set of commands.");
|
||||
Cmd_SetPure ("if");
|
||||
Cmd_AddCommand ("ifnot", Cmd_If_f, "Conditionally execute a set of commands if the condition is false.");
|
||||
Cmd_AddCommand ("while", Cmd_While_f, "Execute a set of commands while a condition is true.");
|
||||
Cmd_SetPure ("while");
|
||||
|
|
|
@ -32,19 +32,22 @@ exp_error_t EXP_ERROR;
|
|||
|
||||
optable_t optable[] =
|
||||
{
|
||||
{"**", OP_Exp},
|
||||
{"/", OP_Div},
|
||||
{"*", OP_Mult},
|
||||
{"+", OP_Add},
|
||||
{"-", OP_Sub},
|
||||
{"==", OP_Eq},
|
||||
{"!=", OP_Neq},
|
||||
{">=", OP_GreaterThanEqual},
|
||||
{">", OP_GreaterThan},
|
||||
{"<=", OP_LessThanEqual},
|
||||
{"<", OP_LessThan},
|
||||
{"||", OP_Or},
|
||||
{"&&", OP_And},
|
||||
{"!", OP_Not, 1},
|
||||
{"**", OP_Exp, 2},
|
||||
{"/", OP_Div, 2},
|
||||
{"*", OP_Mult, 2},
|
||||
{"+", OP_Add, 2},
|
||||
{"-", OP_Sub, 2},
|
||||
{"==", OP_Eq, 2},
|
||||
{"!=", OP_Neq, 2},
|
||||
{">=", OP_GreaterThanEqual, 2},
|
||||
{">", OP_GreaterThan, 2},
|
||||
{"<=", OP_LessThanEqual, 2},
|
||||
{"<", OP_LessThan, 2},
|
||||
{"||", OP_Or, 2},
|
||||
{"or", OP_Or, 2},
|
||||
{"&&", OP_And, 2},
|
||||
{"and", OP_And, 2},
|
||||
{"", 0}
|
||||
};
|
||||
|
||||
|
@ -60,12 +63,33 @@ token *EXP_NewToken (void)
|
|||
return new;
|
||||
}
|
||||
|
||||
int EXP_FindIndexByFunc (opfunc func)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; optable[i].func; i++)
|
||||
if (func == optable[i].func)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int EXP_FindIndexByStr (const char *str)
|
||||
{
|
||||
int i, len, fi;
|
||||
|
||||
for (i = 0, len = 0, fi = -1; optable[i].func; i++)
|
||||
if (!strncmp(str, optable[i].str, strlen(optable[i].str)) && strlen(optable[i].str) > len) {
|
||||
len = strlen(optable[i].str);
|
||||
fi = i;
|
||||
}
|
||||
return fi;
|
||||
}
|
||||
|
||||
token *EXP_ParseString (char *str)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
token *chain, *new, *cur;
|
||||
int i,n,m;
|
||||
int i,m,fi;
|
||||
|
||||
cur = chain = EXP_NewToken();
|
||||
chain->generic.type = TOKEN_OPAREN;
|
||||
|
@ -75,8 +99,10 @@ token *EXP_ParseString (char *str)
|
|||
for (i = 0; i < strlen(str); i++)
|
||||
{
|
||||
m = 0;
|
||||
while(str[i] == ' ')
|
||||
while(isspace((byte)str[i]))
|
||||
i++;
|
||||
if (!str[i])
|
||||
break;
|
||||
if (isdigit((byte)str[i]) || str[i] == '.')
|
||||
{
|
||||
while ((isdigit((byte)str[i]) || str[i] == '.') && i < strlen(str) && m < 256)
|
||||
|
@ -111,27 +137,23 @@ token *EXP_ParseString (char *str)
|
|||
}
|
||||
else
|
||||
{
|
||||
while(!(isdigit((byte)str[i])) && str[i] != '.' && str[i] != '(' && str[i] != ')' && m < 256)
|
||||
while(!(isdigit((byte)str[i])) && !isspace((byte)str[i]) && str[i] != '.' && str[i] != '(' && str[i] != ')' && m < 256) {
|
||||
buf[m++] = str[i++];
|
||||
}
|
||||
buf[m] = 0;
|
||||
if (m)
|
||||
{
|
||||
for (n = 0; optable[n].func; n++)
|
||||
{
|
||||
if (!(strncmp(optable[n].str, buf, strlen(optable[n].str))))
|
||||
{
|
||||
i -= (m - strlen(optable[n].str) + 1);
|
||||
fi = EXP_FindIndexByStr (buf);
|
||||
if (fi != -1) {
|
||||
i -= (m - strlen(optable[fi].str) + 1);
|
||||
new = EXP_NewToken();
|
||||
new->generic.type = TOKEN_OP;
|
||||
new->op.func = optable[n].func;
|
||||
new->op.func = optable[fi].func;
|
||||
new->generic.prev = cur;
|
||||
new->generic.next = 0;
|
||||
cur->generic.next = new;
|
||||
cur = new;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(optable[n].func)) {
|
||||
} else {
|
||||
EXP_DestroyTokens (chain);
|
||||
return 0;
|
||||
}
|
||||
|
@ -172,8 +194,14 @@ void EXP_SimplifyTokens (token *chain)
|
|||
{
|
||||
for (cur = chain->generic.next; cur->generic.type != TOKEN_CPAREN; cur = cur->generic.next)
|
||||
{
|
||||
if (cur->generic.type == TOKEN_OP && cur->op.func == optable[i].func && cur->generic.next)
|
||||
if (cur->generic.prev->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_NUM)
|
||||
if (cur->generic.type == TOKEN_OP && cur->op.func == optable[i].func && cur->generic.next) {
|
||||
if (optable[i].operands == 1 && cur->generic.next->generic.type == TOKEN_NUM) {
|
||||
cur->generic.next->num.value = optable[i].func(cur->generic.next->num.value, 0);
|
||||
temp = cur;
|
||||
cur = cur->generic.next;
|
||||
EXP_RemoveToken(temp);
|
||||
}
|
||||
else if (cur->generic.prev->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_NUM)
|
||||
{
|
||||
cur->generic.prev->num.value = optable[i].func(cur->generic.prev->num.value, cur->generic.next->num.value);
|
||||
temp = cur;
|
||||
|
@ -184,6 +212,7 @@ void EXP_SimplifyTokens (token *chain)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EXP_RemoveToken (token *tok)
|
||||
{
|
||||
|
@ -273,7 +302,7 @@ exp_error_t EXP_Validate (token *chain)
|
|||
new->op.func = OP_Mult;
|
||||
EXP_InsertTokenAfter (cur, new);
|
||||
}
|
||||
else
|
||||
else if (optable[EXP_FindIndexByFunc(cur->generic.next->op.func)].operands == 2)
|
||||
return EXP_E_SYNTAX; /* Operator misuse */
|
||||
}
|
||||
else if (cur->generic.type == TOKEN_OP && cur->generic.next->generic.type == TOKEN_CPAREN)
|
||||
|
@ -312,4 +341,5 @@ void EXP_PrintTokens (token *chain)
|
|||
case TOKEN_GENERIC:
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
float OP_Not (float op1, float op2)
|
||||
{
|
||||
return !op1;
|
||||
}
|
||||
|
||||
float OP_Add (float op1, float op2)
|
||||
{
|
||||
return op1 + op2;
|
||||
|
|
Loading…
Reference in a new issue