From ffee440fa04669d3ca2b9d4c285e46f872e16031 Mon Sep 17 00:00:00 2001 From: ac-stef Date: Wed, 7 Jun 2017 18:16:06 +0200 Subject: [PATCH] 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 --- config/menus_multiplayer.cfg | 39 +++++----- source/src/command.cpp | 134 +++++++++++++++++++++++------------ 2 files changed, 108 insertions(+), 65 deletions(-) diff --git a/config/menus_multiplayer.cfg b/config/menus_multiplayer.cfg index 8832d191..f6dd2a1d 100644 --- a/config/menus_multiplayer.cfg +++ b/config/menus_multiplayer.cfg @@ -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 ] diff --git a/source/src/command.cpp b/source/src/command.cpp index 70bfcf10..c1ef7ef0 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -15,6 +15,7 @@ int loop_level = 0; // avoid bad calls of br hashtable *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 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 &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 {