add early string replacement and alias lookup to cubescript

replace tokens starting with @ during scanning/sizing-up of []-blocks

  @word    look up word as in $word
  @(...)   evaluate braces
  @[...]   evaluate braces and look up result
  @"..."   parse quotes

in nested []-blocks, use multiple @ to determine time of replacement
(for example, @@@word is replaced when found inside three nested braces)

in regular $-lookup, $[...] now evaluates the braces before lookup
This commit is contained in:
ac-stef
2017-06-07 18:16:06 +02:00
parent 4d684cfc8e
commit ffee440fa0
2 changed files with 108 additions and 65 deletions
+19 -20
View File
@@ -16,10 +16,10 @@ const joinservermenu [
tempalias __ingamemenu_1 [
push t (player1 team)
menuitemtextinput "Nickname: " "result $curname" [ name $arg1 ]
menuitem "Change weapon\i\3" [ showmenu Weapons ] [ __ingamemenu_showmodel 0 ]
menuitem "Change player skin\i\3" (concat showmenu (at [CLA RVSF] (&b $t 1))) [ __ingamemenu_showmodel 1 ]
menuitem "Change to the enemy team" changeteam [ __ingamemenu_showmodel 0 ]
menuitem (concat Change to (at "spectate active" (> $t 1)) mode) spectate
menuitem "Change weapon\i\3" [ showmenu Weapons ] [ __ingamemenu_showmodel 0 ]
menuitem "Change player skin\i\3" [showmenu @(at [CLA RVSF] (&b $t 1))] [ __ingamemenu_showmodel 1 ]
menuitem "Change to the enemy team" changeteam [ __ingamemenu_showmodel 0 ]
menuitem [Change to @(at "spectate active" (> $t 1)) mode] spectate
pop t
]
@@ -35,7 +35,7 @@ tempalias __ingamemenu_2 [
const __ingamemenu_showmodel [
if (&& (< (player1 team) 4) $arg1) [
menumdl (curmenu) (concatword "playermodels/" (at [CLA RVSF CLA RVSF] (player1 team)) "/0" (+ 1 (player1 skin))) idle 15 3
menumdl (curmenu) [playermodels/@(at [CLA RVSF CLA RVSF] (player1 team))/0@(+ 1 (player1 skin))] idle 15 3
] [
menumdl (curmenu)
]
@@ -180,7 +180,6 @@ menuitem "\f2\t\t [ Admin connect ]\t\t" [ connectadmin $acc1 $acc2 $acc3 ]
//// Main > Multiplayer > server > serverbrowser help ////
const __getfavattr [ push n (concatword sbfavourite_ $arg1 _ $arg2) ; n = $$n ; result (pop n) ]
newmenu [serverbrowser help]
menuinitselection 1
menuinit [
@@ -208,7 +207,7 @@ menuinit [
menuitem "Keys to add/remove servers from categories:" -1
looplisti (listfavcats) fav [
if (< $i 10) [
menuitem (concatword (+ $i 1) ": Toggle category " (__getfavattr $fav desc) " <\fs" (__getfavattr $fav tag) "\fr>") -1
menuitem [@(+ $i 1): Toggle category @[sbfavourite_@[fav]_desc] @"<\fs"@[sbfavourite_@[fav]_tag]@"\fr>"] -1
]
]
]
@@ -237,9 +236,9 @@ menuinit [
resetcurmenu
looplisti (listfavcats) fav [
if (< $i 10) [
menuitem (concatword (+ 1 (mod $i 10)) ": " (__getfavattr $fav desc) " <\fs" (__getfavattr $fav tag) "\fr>") (concatword "__curfav = " $i " ; showmenu [edit favourites category]")
menuitem [@(+ 1 (mod $i 10)): @[sbfavourite_@[fav]_desc] @"<\fs"@[sbfavourite_@[fav]_tag]@"\fr>"] [__curfav = @i; showmenu [edit favourites category]]
] [
menuitem (concatword (__getfavattr $fav desc) " <\fs" (__getfavattr $fav tag) "\fr>") (concatword "__curfav = " $i " ; showmenu [edit favourites category]")
menuitem [@[sbfavourite_@[fav]_desc] @"<\fs"@[sbfavourite_@[fav]_tag]@"\fr>"] [__curfav = @i; showmenu [edit favourites category]]
]
]
]
@@ -251,23 +250,23 @@ menuinit [
resetcurmenu
push fav (at (listfavcats) $__curfav)
push n (+ 1 $__curfav)
menuitem (concatword "Category #" $n ", name: \f2" $fav) -1
menuitem [Category #@n@", name: \f2"@fav] -1
menuitem "" -1
menuitemtextinput "Tag: " (concatword "escapenoquotes $" sbfavourite_ $fav _tag) (concatword sbfavourite_ $fav _tag " = (at (addpunct $arg1) 0)") [] 15
menuitemtextinput "Description: " (concatword "escapenoquotes $" sbfavourite_ $fav _desc) (concatword sbfavourite_ $fav _desc " = (at (addpunct $arg1) 0)") [] 45
menuitemtextinput "Tag: " [escapenoquotes $sbfavourite_@[fav]_tag] [sbfavourite_@[fav]_tag = (at (addpunct $arg1) 0)] [] 15
menuitemtextinput "Description: " [escapenoquotes $sbfavourite_@[fav]_desc] [sbfavourite_@[fav]_desc = (at (addpunct $arg1) 0)] [] 45
menuitem "" -1
menuitemslider "Background red: \t" 0 100 (__getfavattr $fav red) 10 (concatword sbfavourite_ $fav _red " = $arg1") 1
menuitemslider "Background green:\t" 0 100 (__getfavattr $fav green) 10 (concatword sbfavourite_ $fav _green " = $arg1") 1
menuitemslider "Background blue: \t" 0 100 (__getfavattr $fav blue) 10 (concatword sbfavourite_ $fav _blue " = $arg1") 1
menuitemslider "Background alpha:\t" 0 100 (__getfavattr $fav alpha) 10 (concatword sbfavourite_ $fav _alpha " = $arg1") 1
menuitemslider "Background red: \t" 0 100 $[sbfavourite_@[fav]_red] 10 [sbfavourite_@[fav]_red = $arg1] 1
menuitemslider "Background green:\t" 0 100 $[sbfavourite_@[fav]_green] 10 [sbfavourite_@[fav]_green = $arg1] 1
menuitemslider "Background blue: \t" 0 100 $[sbfavourite_@[fav]_blue] 10 [sbfavourite_@[fav]_blue = $arg1] 1
menuitemslider "Background alpha:\t" 0 100 $[sbfavourite_@[fav]_alpha] 10 [sbfavourite_@[fav]_alpha = $arg1] 1
menuitem "" -1
menuitemtextinput "Keyword list:" (concatword "result $" sbfavourite_ $fav _keys) (concatword sbfavourite_ $fav _keys " = $arg1") [] 255
menuitemtextinput "Keyword list:" [result $sbfavourite_@[fav]_keys] [sbfavourite_@[fav]_keys = $arg1] [] 255
menuitem "" -1
menuitemtextinput "Weight:" (concatword "result $" sbfavourite_ $fav _weight) (concatword sbfavourite_ $fav _weight " = $arg1") [] 10
menuitemtextinput "Weight:" [result $sbfavourite_@[fav]_weight] [sbfavourite_@[fav]_weight = $arg1] [] 10
menuitem "" -1
menuitemradio "" 0 1 (__getfavattr $fav ignore) ["Enable this category " "Ignore this category"] (concatword sbfavourite_ $fav _ignore " = $arg1")
menuitemradio "" 0 1 $[sbfavourite_@[fav]_ignore] ["Enable this category " "Ignore this category"] [sbfavourite_@[fav]_ignore = $arg1]
menuitem "" -1
menuitem "Show servers of this category\i\3" (concat showonlyfavourites $n ";showmenu server") [ showonlyfavourites 0 ]
menuitem "Show servers of this category\i\3" [showonlyfavourites @n ; showmenu server] [ showonlyfavourites 0 ]
pop fav n
]
+89 -45
View File
@@ -15,6 +15,7 @@ int loop_level = 0; // avoid bad calls of br
hashtable<const char *, ident> *idents = NULL; // contains ALL vars/commands/aliases
bool persistidents = false;
bool currentcontextisolated = false;
COMMANDF(persistidents, "s", (char *on)
{
@@ -388,13 +389,35 @@ bool addcommand(const char *name, void (*fun)(), const char *sig)
return false;
}
inline char *parsequotes(const char *&p)
{
const char *word = p + 1;
do
{
p++;
p += strcspn(p, "\"\n\r");
}
while(*p == '\"' && p[-1] == '\\'); // skip escaped quotes
char *s = newstring(word, p - word);
#ifndef STANDALONE
filterrichtext(s, s, p - word);
#endif
if(*p=='\"') p++;
return s;
}
char *lookup(char *n, int levels);
char *parseexp(const char *&p, int right) // parse any nested set of () or []
{
vector<char> res;
int left = *p++;
const char *word = p;
bool quot = false;
char *s;
bool quot = false, issq = left == '[';
for(int brak = 1; brak; )
{
p += strcspn(p, "([\"])@");
int c = *p++;
if(c==left && !quot) brak++;
else if(c=='"') quot = !quot;
@@ -403,12 +426,45 @@ char *parseexp(const char *&p, int right) // parse any nested set of
{
p--;
conoutf("missing \"%c\"", right);
scripterr();
flagmapconfigerror(LWW_SCRIPTERR * 4);
return NULL;
goto screrr;
}
else if(issq && c == '@' && !quot)
{
const char *sq = p;
while(*p == '@') p++;
int level = p - sq + 1;
if(level == brak)
{
res.put(word, sq - word - 1);
char *n, a = *p;
switch(a)
{
case '(': n = parseexp(p, ')'); break;
case '"': n = parsequotes(p); break;
case '[':
n = parseexp(p, ']');
if(n) n = lookup(n, 1);
break;
default:
{
const char *w = p;
p += strcspn(p, "])@; \t\n\r");
n = lookup(newstring(w, p - w), 1);
break;
}
}
if(n) res.put(n, strlen(n)), delete[] n;
word = p;
}
else if(level > brak) { conoutf("too many @"); goto screrr; }
}
}
char *s = newstring(word, p-word-1);
if(res.length())
{
res.put(word, p - word - 1);
return newstring(res.getbuf(), res.length());
}
s = newstring(word, p-word-1);
if(left=='(')
{
char *ret = executeret(s); // evaluate () exps directly, and substitute result
@@ -416,11 +472,16 @@ char *parseexp(const char *&p, int right) // parse any nested set of
s = ret ? ret : newstring("");
}
return s;
screrr:
scripterr();
flagmapconfigerror(LWW_SCRIPTERR * 4);
return NULL;
}
char *lookup(char *n, int levels) // find value of ident referenced with $ in exp
{
if(levels > 1) n = exchangestr(n, lookup(newstring(n), levels - 1)); // nested ("$$var")
if(levels > 1) n = exchangestr(n, lookup(newstring(n), min(levels - 1, 2))); // nested ("$$var"), limit to three levels
ident *id = idents->access(n);
if(id) switch(id->type)
{
@@ -435,42 +496,24 @@ char *lookup(char *n, int levels) // find value of ide
return n;
}
char *parseword(const char *&p, int arg, int &infix) // parse single argument, including expressions
char *parseword(const char *&p, int arg, int *infix) // parse single argument, including expressions
{
p += strspn(p, " \t");
if(p[0]=='/' && p[1]=='/') p += strcspn(p, "\n\r\0");
if(*p=='\"')
{
const char *word = p + 1;
do
{
p++;
p += strcspn(p, "\"\n\r");
}
while(*p == '\"' && p[-1] == '\\'); // skip escaped quotes
char *s = newstring(word, p - word);
if(*p=='\"') p++;
#ifndef STANDALONE
filterrichtext(s, s, strlen(s));
#endif
return s;
}
if(*p=='(') return parseexp(p, ')');
if(*p=='[') return parseexp(p, ']');
int lvls = strspn(p, "$");
if(p[lvls]=='(')
{ // $()
if(*p=='"') return parsequotes(p);
if(*p=='(' && !currentcontextisolated) return parseexp(p, ')');
if(*p=='[' && !currentcontextisolated) return parseexp(p, ']');
int lvls = currentcontextisolated ? 0 : strspn(p, "$");
if(lvls && (p[lvls]=='(' || p[lvls]=='['))
{ // $() $[]
p += lvls;
char *b = parseexp(p, ')');
char *b = parseexp(p, *p == '(' ? ')' : ']');
if(b) return lookup(b, lvls);
}
const char *word = p;
p += strcspn(p, "; \t\n\r\0");
if(p-word==0) return NULL;
if(arg==1 && p-word==1) switch(*word)
{
case '=': infix = *word; break;
}
if(arg == 1 && *word == '=' && p - word == 1) *infix = *word;
return lvls ? lookup(newstring(word + lvls, p - word - lvls), lvls) : newstring(word, p-word);
}
@@ -547,7 +590,7 @@ char *executeret(const char *p) // all evaluation hap
{
w[i] = &emptychar;
if(i>numargs) continue;
char *s = parseword(p, i, infix); // parse and evaluate exps
char *s = parseword(p, i, &infix); // parse and evaluate exps
if(s) w[i] = s;
else numargs = i;
}
@@ -559,16 +602,11 @@ char *executeret(const char *p) // all evaluation hap
DELETEA(retval);
if(infix)
if(infix == '=')
{
switch(infix)
{
case '=':
DELETEA(w[1]);
swap(w[0], w[1]);
c = "alias";
break;
}
DELETEA(w[1]);
swap(w[0], w[1]);
c = "alias";
}
ident *id = idents->access(c);
@@ -1156,9 +1194,12 @@ void explodelist(const char *s, vector<char *> &elems)
{
const char *elem = s;
elementskip;
#ifdef STANDALONE
char *newelem = *elem == '"' ? newstring(elem + 1, s - elem - (s[-1]=='"' ? 2 : 1)) : newstring(elem, s-elem);
#ifndef STANDALONE
if(*elem == '\"') filterrichtext(newelem, newelem, strlen(newelem));
#else
size_t len;
char *newelem = *elem == '"' ? newstring(elem + 1, (len = s - elem - (s[-1]=='"' ? 2 : 1))) : newstring(elem, s-elem);
if(*elem == '\"') filterrichtext(newelem, newelem, len);
#endif
elems.add(newelem);
whitespaceskip;
@@ -1655,8 +1696,10 @@ int execcontext;
void pushscontext(int newcontext)
{
ASSERT(newcontext >= 0 && newcontext < IEXC_NUM);
contextstack.add(execcontext);
execcontext = newcontext;
currentcontextisolated = contextsealed && contextisolated[execcontext];
}
int popscontext()
@@ -1664,6 +1707,7 @@ int popscontext()
ASSERT(contextstack.length() > 0);
int old = execcontext;
execcontext = contextstack.pop();
currentcontextisolated = contextsealed && contextisolated[execcontext];
if(execcontext < old && old >= IEXC_MAPCFG) // clean up aliases created in the old (map cfg) context
{