2020-04-15 18:39:17 +02:00
// command.cpp: implements the parsing and execution of a tiny script language which
// is largely backwards compatible with the quake console language.
2020-06-10 02:10:22 +02:00
# include "command.hh"
2020-06-22 23:45:20 +02:00
2020-07-30 04:21:19 +02:00
# include <cctype>
2020-07-30 04:28:27 +02:00
# include <climits>
2020-07-30 04:21:19 +02:00
2020-07-30 04:11:45 +02:00
# include <sauerlib/encoding.hh>
2020-07-29 05:20:21 +02:00
# include <shared/igame.hh>
2020-06-22 23:45:20 +02:00
# include "console.hh"
2020-07-29 03:41:48 +02:00
# include "main.hh" // fatal, timings
2020-06-22 23:45:20 +02:00
2020-04-15 18:39:17 +02:00
hashnameset < ident > idents ; // contains ALL vars/commands/aliases
vector < ident * > identmap ;
2020-07-30 03:15:44 +02:00
ident * dummyident = nullptr ;
2020-04-15 18:39:17 +02:00
int identflags = 0 ;
enum
{
MAXARGS = 25 ,
MAXRESULTS = 7 ,
MAXCOMARGS = 12
} ;
VARN ( numargs , _numargs , MAXARGS , 0 , 0 ) ;
static inline void freearg ( tagval & v )
{
switch ( v . type )
{
case VAL_STR : delete [ ] v . s ; break ;
case VAL_CODE : if ( v . code [ - 1 ] = = CODE_START ) delete [ ] ( uchar * ) & v . code [ - 1 ] ; break ;
}
}
static inline void forcenull ( tagval & v )
{
switch ( v . type )
{
case VAL_NULL : return ;
}
freearg ( v ) ;
v . setnull ( ) ;
}
static inline float forcefloat ( tagval & v )
{
float f = 0.0f ;
switch ( v . type )
{
case VAL_INT : f = v . i ; break ;
case VAL_STR : case VAL_MACRO : case VAL_CSTR : f = parsefloat ( v . s ) ; break ;
case VAL_FLOAT : return v . f ;
}
freearg ( v ) ;
v . setfloat ( f ) ;
return f ;
}
static inline int forceint ( tagval & v )
{
int i = 0 ;
switch ( v . type )
{
case VAL_FLOAT : i = v . f ; break ;
case VAL_STR : case VAL_MACRO : case VAL_CSTR : i = parseint ( v . s ) ; break ;
case VAL_INT : return v . i ;
}
freearg ( v ) ;
v . setint ( i ) ;
return i ;
}
static inline const char * forcestr ( tagval & v )
{
const char * s = " " ;
switch ( v . type )
{
case VAL_FLOAT : s = floatstr ( v . f ) ; break ;
case VAL_INT : s = intstr ( v . i ) ; break ;
case VAL_MACRO : case VAL_CSTR : s = v . s ; break ;
case VAL_STR : return v . s ;
}
freearg ( v ) ;
v . setstr ( newstring ( s ) ) ;
return s ;
}
static inline void forcearg ( tagval & v , int type )
{
switch ( type )
{
case RET_STR : if ( v . type ! = VAL_STR ) forcestr ( v ) ; break ;
case RET_INT : if ( v . type ! = VAL_INT ) forceint ( v ) ; break ;
case RET_FLOAT : if ( v . type ! = VAL_FLOAT ) forcefloat ( v ) ; break ;
}
}
void tagval : : cleanup ( )
{
freearg ( * this ) ;
}
static inline void freeargs ( tagval * args , int & oldnum , int newnum )
{
for ( int i = newnum ; i < oldnum ; i + + ) freearg ( args [ i ] ) ;
oldnum = newnum ;
}
static inline void cleancode ( ident & id )
{
if ( id . code )
{
id . code [ 0 ] - = 0x100 ;
if ( int ( id . code [ 0 ] ) < 0x100 ) delete [ ] id . code ;
2020-07-30 03:15:44 +02:00
id . code = nullptr ;
2020-04-15 18:39:17 +02:00
}
}
struct nullval : tagval
{
nullval ( ) { setnull ( ) ; }
} nullval ;
tagval noret = nullval , * commandret = & noret ;
void clear_command ( )
{
enumerate ( idents , ident , i ,
{
if ( i . type = = ID_ALIAS )
{
DELETEA ( i . name ) ;
i . forcenull ( ) ;
DELETEA ( i . code ) ;
}
} ) ;
}
void clearoverride ( ident & i )
{
if ( ! ( i . flags & IDF_OVERRIDDEN ) ) return ;
switch ( i . type )
{
case ID_ALIAS :
if ( i . valtype = = VAL_STR )
{
if ( ! i . val . s [ 0 ] ) break ;
delete [ ] i . val . s ;
}
cleancode ( i ) ;
i . valtype = VAL_STR ;
i . val . s = newstring ( " " ) ;
break ;
case ID_VAR :
* i . storage . i = i . overrideval . i ;
i . changed ( ) ;
break ;
case ID_FVAR :
* i . storage . f = i . overrideval . f ;
i . changed ( ) ;
break ;
case ID_SVAR :
delete [ ] * i . storage . s ;
* i . storage . s = i . overrideval . s ;
i . changed ( ) ;
break ;
}
i . flags & = ~ IDF_OVERRIDDEN ;
}
void clearoverrides ( )
{
enumerate ( idents , ident , i , clearoverride ( i ) ) ;
}
static bool initedidents = false ;
2020-07-30 03:15:44 +02:00
static vector < ident > * identinits = nullptr ;
2020-04-15 18:39:17 +02:00
static inline ident * addident ( const ident & id )
{
if ( ! initedidents )
{
if ( ! identinits ) identinits = new vector < ident > ;
identinits - > add ( id ) ;
2020-07-30 03:15:44 +02:00
return nullptr ;
2020-04-15 18:39:17 +02:00
}
ident & def = idents . access ( id . name , id ) ;
def . index = identmap . length ( ) ;
return identmap . add ( & def ) ;
}
static bool initidents ( )
{
initedidents = true ;
for ( int i = 0 ; i < MAXARGS ; i + + )
{
defformatstring ( argname , " arg%d " , i + 1 ) ;
newident ( argname , IDF_ARG ) ;
}
dummyident = newident ( " //dummy " , IDF_UNKNOWN ) ;
if ( identinits )
{
loopv ( * identinits ) addident ( ( * identinits ) [ i ] ) ;
DELETEP ( identinits ) ;
}
return true ;
}
UNUSED static bool forceinitidents = initidents ( ) ;
2020-07-30 03:15:44 +02:00
static const char * sourcefile = nullptr , * sourcestr = nullptr ;
2020-04-15 18:39:17 +02:00
static const char * debugline ( const char * p , const char * fmt )
{
if ( ! sourcestr ) return fmt ;
int num = 1 ;
const char * line = sourcestr ;
for ( ; ; )
{
const char * end = strchr ( line , ' \n ' ) ;
if ( ! end ) end = line + strlen ( line ) ;
if ( p > = line & & p < = end )
{
static string buf ;
if ( sourcefile ) formatstring ( buf , " %s:%d: %s " , sourcefile , num , fmt ) ;
else formatstring ( buf , " %d: %s " , num , fmt ) ;
return buf ;
}
if ( ! * end ) break ;
line = end + 1 ;
num + + ;
}
return fmt ;
}
static struct identlink
{
ident * id ;
identlink * next ;
int usedargs ;
identstack * argstack ;
2020-07-30 03:15:44 +02:00
} noalias = { nullptr , nullptr , ( 1 < < MAXARGS ) - 1 , nullptr } , * aliasstack = & noalias ;
2020-04-15 18:39:17 +02:00
VAR ( dbgalias , 0 , 4 , 1000 ) ;
static void debugalias ( )
{
if ( ! dbgalias ) return ;
int total = 0 , depth = 0 ;
for ( identlink * l = aliasstack ; l ! = & noalias ; l = l - > next ) total + + ;
for ( identlink * l = aliasstack ; l ! = & noalias ; l = l - > next )
{
ident * id = l - > id ;
+ + depth ;
if ( depth < dbgalias ) conoutf ( CON_ERROR , " %d) %s " , total - depth + 1 , id - > name ) ;
else if ( l - > next = = & noalias ) conoutf ( CON_ERROR , depth = = dbgalias ? " %d) %s " : " ..%d) %s " , total - depth + 1 , id - > name ) ;
}
}
static int nodebug = 0 ;
static void debugcode ( const char * fmt , . . . ) PRINTFARGS ( 1 , 2 ) ;
static void debugcode ( const char * fmt , . . . )
{
if ( nodebug ) return ;
va_list args ;
va_start ( args , fmt ) ;
conoutfv ( CON_ERROR , fmt , args ) ;
va_end ( args ) ;
debugalias ( ) ;
}
static void debugcodeline ( const char * p , const char * fmt , . . . ) PRINTFARGS ( 2 , 3 ) ;
static void debugcodeline ( const char * p , const char * fmt , . . . )
{
if ( nodebug ) return ;
va_list args ;
va_start ( args , fmt ) ;
conoutfv ( CON_ERROR , debugline ( p , fmt ) , args ) ;
va_end ( args ) ;
debugalias ( ) ;
}
ICOMMAND ( nodebug , " e " , ( uint * body ) , { nodebug + + ; executeret ( body , * commandret ) ; nodebug - - ; } ) ;
void addident ( ident * id )
{
addident ( * id ) ;
}
static inline void pusharg ( ident & id , const tagval & v , identstack & stack )
{
stack . val = id . val ;
stack . valtype = id . valtype ;
stack . next = id . stack ;
id . stack = & stack ;
id . setval ( v ) ;
cleancode ( id ) ;
}
static inline void poparg ( ident & id )
{
if ( ! id . stack ) return ;
identstack * stack = id . stack ;
if ( id . valtype = = VAL_STR ) delete [ ] id . val . s ;
id . setval ( * stack ) ;
cleancode ( id ) ;
id . stack = stack - > next ;
}
static inline void undoarg ( ident & id , identstack & stack )
{
identstack * prev = id . stack ;
stack . val = id . val ;
stack . valtype = id . valtype ;
stack . next = prev ;
id . stack = prev - > next ;
id . setval ( * prev ) ;
cleancode ( id ) ;
}
# define UNDOFLAG (1<<MAXARGS)
# define UNDOARGS \
identstack argstack [ MAXARGS ] ; \
identlink * prevstack = aliasstack ; \
identlink aliaslink ; \
for ( int undos = 0 ; prevstack ! = & noalias ; prevstack = prevstack - > next ) \
{ \
if ( prevstack - > usedargs & UNDOFLAG ) + + undos ; \
else if ( undos > 0 ) - - undos ; \
else \
{ \
prevstack = prevstack - > next ; \
for ( int argmask = aliasstack - > usedargs & ~ UNDOFLAG , i = 0 ; argmask ; argmask > > = 1 , i + + ) if ( argmask & 1 ) \
undoarg ( * identmap [ i ] , argstack [ i ] ) ; \
aliaslink . id = aliasstack - > id ; \
aliaslink . next = aliasstack ; \
aliaslink . usedargs = UNDOFLAG | prevstack - > usedargs ; \
aliaslink . argstack = prevstack - > argstack ; \
aliasstack = & aliaslink ; \
break ; \
} \
} \
static inline void redoarg ( ident & id , const identstack & stack )
{
identstack * prev = stack . next ;
prev - > val = id . val ;
prev - > valtype = id . valtype ;
id . stack = prev ;
id . setval ( stack ) ;
cleancode ( id ) ;
}
# define REDOARGS \
if ( aliasstack = = & aliaslink ) \
{ \
prevstack - > usedargs | = aliaslink . usedargs & ~ UNDOFLAG ; \
aliasstack = aliaslink . next ; \
for ( int argmask = aliasstack - > usedargs & ~ UNDOFLAG , i = 0 ; argmask ; argmask > > = 1 , i + + ) if ( argmask & 1 ) \
redoarg ( * identmap [ i ] , argstack [ i ] ) ; \
}
ICOMMAND ( push , " rTe " , ( ident * id , tagval * v , uint * code ) ,
{
if ( id - > type ! = ID_ALIAS | | id - > index < MAXARGS ) return ;
identstack stack ;
pusharg ( * id , * v , stack ) ;
v - > type = VAL_NULL ;
id - > flags & = ~ IDF_UNKNOWN ;
executeret ( code , * commandret ) ;
poparg ( * id ) ;
} ) ;
static inline void pushalias ( ident & id , identstack & stack )
{
if ( id . type = = ID_ALIAS & & id . index > = MAXARGS )
{
pusharg ( id , nullval , stack ) ;
id . flags & = ~ IDF_UNKNOWN ;
}
}
static inline void popalias ( ident & id )
{
if ( id . type = = ID_ALIAS & & id . index > = MAXARGS ) poparg ( id ) ;
}
KEYWORD ( local , ID_LOCAL ) ;
static inline bool checknumber ( const char * s )
{
if ( isdigit ( s [ 0 ] ) ) return true ;
else switch ( s [ 0 ] )
{
case ' + ' : case ' - ' : return isdigit ( s [ 1 ] ) | | ( s [ 1 ] = = ' . ' & & isdigit ( s [ 2 ] ) ) ;
case ' . ' : return isdigit ( s [ 1 ] ) ! = 0 ;
default : return false ;
}
}
static inline bool checknumber ( const stringslice & s ) { return checknumber ( s . str ) ; }
template < class T > static inline ident * newident ( const T & name , int flags )
{
ident * id = idents . access ( name ) ;
if ( ! id )
{
if ( checknumber ( name ) )
{
debugcode ( " number %.*s is not a valid identifier name " , stringlen ( name ) , stringptr ( name ) ) ;
return dummyident ;
}
id = addident ( ident ( ID_ALIAS , newstring ( name ) , flags ) ) ;
}
return id ;
}
static inline ident * forceident ( tagval & v )
{
switch ( v . type )
{
case VAL_IDENT : return v . id ;
case VAL_MACRO : case VAL_CSTR :
{
ident * id = newident ( v . s , IDF_UNKNOWN ) ;
v . setident ( id ) ;
return id ;
}
case VAL_STR :
{
ident * id = newident ( v . s , IDF_UNKNOWN ) ;
delete [ ] v . s ;
v . setident ( id ) ;
return id ;
}
}
freearg ( v ) ;
v . setident ( dummyident ) ;
return dummyident ;
}
ident * newident ( const char * name , int flags )
{
return newident < const char * > ( name , flags ) ;
}
ident * writeident ( const char * name , int flags )
{
ident * id = newident ( name , flags ) ;
if ( id - > index < MAXARGS & & ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) )
{
pusharg ( * id , nullval , aliasstack - > argstack [ id - > index ] ) ;
aliasstack - > usedargs | = 1 < < id - > index ;
}
return id ;
}
ident * readident ( const char * name )
{
ident * id = idents . access ( name ) ;
if ( id & & id - > index < MAXARGS & & ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) )
2020-07-30 03:15:44 +02:00
return nullptr ;
2020-04-15 18:39:17 +02:00
return id ;
}
void resetvar ( char * name )
{
ident * id = idents . access ( name ) ;
if ( ! id ) return ;
if ( id - > flags & IDF_READONLY ) debugcode ( " variable %s is read-only " , id - > name ) ;
else clearoverride ( * id ) ;
}
COMMAND ( resetvar , " s " ) ;
static inline void setarg ( ident & id , tagval & v )
{
if ( aliasstack - > usedargs & ( 1 < < id . index ) )
{
if ( id . valtype = = VAL_STR ) delete [ ] id . val . s ;
id . setval ( v ) ;
cleancode ( id ) ;
}
else
{
pusharg ( id , v , aliasstack - > argstack [ id . index ] ) ;
aliasstack - > usedargs | = 1 < < id . index ;
}
}
static inline void setalias ( ident & id , tagval & v )
{
if ( id . valtype = = VAL_STR ) delete [ ] id . val . s ;
id . setval ( v ) ;
cleancode ( id ) ;
id . flags = ( id . flags & identflags ) | identflags ;
}
static void setalias ( const char * name , tagval & v )
{
ident * id = idents . access ( name ) ;
if ( id )
{
switch ( id - > type )
{
case ID_ALIAS :
if ( id - > index < MAXARGS ) setarg ( * id , v ) ; else setalias ( * id , v ) ;
return ;
case ID_VAR :
setvarchecked ( id , v . getint ( ) ) ;
break ;
case ID_FVAR :
setfvarchecked ( id , v . getfloat ( ) ) ;
break ;
case ID_SVAR :
setsvarchecked ( id , v . getstr ( ) ) ;
break ;
default :
debugcode ( " cannot redefine builtin %s with an alias " , id - > name ) ;
break ;
}
freearg ( v ) ;
}
else if ( checknumber ( name ) )
{
debugcode ( " cannot alias number %s " , name ) ;
freearg ( v ) ;
}
else
{
addident ( ident ( ID_ALIAS , newstring ( name ) , v , identflags ) ) ;
}
}
void alias ( const char * name , const char * str )
{
tagval v ;
v . setstr ( newstring ( str ) ) ;
setalias ( name , v ) ;
}
void alias ( const char * name , tagval & v )
{
setalias ( name , v ) ;
}
ICOMMAND ( alias , " sT " , ( const char * name , tagval * v ) ,
{
setalias ( name , * v ) ;
v - > type = VAL_NULL ;
} ) ;
// variable's and commands are registered through globals, see cube.h
int variable ( const char * name , int min , int cur , int max , int * storage , identfun fun , int flags )
{
addident ( ident ( ID_VAR , name , min , max , storage , ( void * ) fun , flags ) ) ;
return cur ;
}
float fvariable ( const char * name , float min , float cur , float max , float * storage , identfun fun , int flags )
{
addident ( ident ( ID_FVAR , name , min , max , storage , ( void * ) fun , flags ) ) ;
return cur ;
}
char * svariable ( const char * name , const char * cur , char * * storage , identfun fun , int flags )
{
addident ( ident ( ID_SVAR , name , storage , ( void * ) fun , flags ) ) ;
return newstring ( cur ) ;
}
struct defvar : identval
{
char * name ;
uint * onchange ;
2020-07-30 03:15:44 +02:00
defvar ( ) : name ( nullptr ) , onchange ( nullptr ) { }
2020-04-15 18:39:17 +02:00
~ defvar ( )
{
DELETEA ( name ) ;
if ( onchange ) freecode ( onchange ) ;
}
static void changed ( ident * id )
{
defvar * v = ( defvar * ) id - > storage . p ;
if ( v - > onchange ) execute ( v - > onchange ) ;
}
} ;
hashnameset < defvar > defvars ;
# define DEFVAR(cmdname, fmt, args, body) \
ICOMMAND ( cmdname , fmt , args , \
{ \
if ( idents . access ( name ) ) { debugcode ( " cannot redefine %s as a variable " , name ) ; return ; } \
name = newstring ( name ) ; \
defvar & def = defvars [ name ] ; \
def . name = name ; \
2020-07-30 03:15:44 +02:00
def . onchange = onchange [ 0 ] ? compilecode ( onchange ) : nullptr ; \
2020-04-15 18:39:17 +02:00
body ; \
} ) ;
# define DEFIVAR(cmdname, flags) \
DEFVAR ( cmdname , " siiis " , ( char * name , int * min , int * cur , int * max , char * onchange ) , \
2020-07-30 03:15:44 +02:00
def . i = variable ( name , * min , * cur , * max , & def . i , def . onchange ? defvar : : changed : nullptr , flags ) )
2020-04-15 18:39:17 +02:00
# define DEFFVAR(cmdname, flags) \
DEFVAR ( cmdname , " sfffs " , ( char * name , float * min , float * cur , float * max , char * onchange ) , \
2020-07-30 03:15:44 +02:00
def . f = fvariable ( name , * min , * cur , * max , & def . f , def . onchange ? defvar : : changed : nullptr , flags ) )
2020-04-15 18:39:17 +02:00
# define DEFSVAR(cmdname, flags) \
DEFVAR ( cmdname , " sss " , ( char * name , char * cur , char * onchange ) , \
2020-07-30 03:15:44 +02:00
def . s = svariable ( name , cur , & def . s , def . onchange ? defvar : : changed : nullptr , flags ) )
2020-04-15 18:39:17 +02:00
DEFIVAR ( defvar , 0 ) ;
DEFIVAR ( defvarp , IDF_PERSIST ) ;
DEFFVAR ( deffvar , 0 ) ;
DEFFVAR ( deffvarp , IDF_PERSIST ) ;
DEFSVAR ( defsvar , 0 ) ;
DEFSVAR ( defsvarp , IDF_PERSIST ) ;
# define _GETVAR(id, vartype, name, retval) \
ident * id = idents . access ( name ) ; \
if ( ! id | | id - > type ! = vartype ) return retval ;
# define GETVAR(id, name, retval) _GETVAR(id, ID_VAR, name, retval)
# define OVERRIDEVAR(errorval, saveval, resetval, clearval) \
if ( identflags & IDF_OVERRIDDEN | | id - > flags & IDF_OVERRIDE ) \
{ \
if ( id - > flags & IDF_PERSIST ) \
{ \
debugcode ( " cannot override persistent variable %s " , id - > name ) ; \
errorval ; \
} \
if ( ! ( id - > flags & IDF_OVERRIDDEN ) ) { saveval ; id - > flags | = IDF_OVERRIDDEN ; } \
else { clearval ; } \
} \
else \
{ \
if ( id - > flags & IDF_OVERRIDDEN ) { resetval ; id - > flags & = ~ IDF_OVERRIDDEN ; } \
clearval ; \
}
void setvar ( const char * name , int i , bool dofunc , bool doclamp )
{
GETVAR ( id , name , ) ;
OVERRIDEVAR ( return , id - > overrideval . i = * id - > storage . i , , )
if ( doclamp ) * id - > storage . i = clamp ( i , id - > minval , id - > maxval ) ;
else * id - > storage . i = i ;
if ( dofunc ) id - > changed ( ) ;
}
void setfvar ( const char * name , float f , bool dofunc , bool doclamp )
{
_GETVAR ( id , ID_FVAR , name , ) ;
OVERRIDEVAR ( return , id - > overrideval . f = * id - > storage . f , , ) ;
if ( doclamp ) * id - > storage . f = clamp ( f , id - > minvalf , id - > maxvalf ) ;
else * id - > storage . f = f ;
if ( dofunc ) id - > changed ( ) ;
}
void setsvar ( const char * name , const char * str , bool dofunc )
{
_GETVAR ( id , ID_SVAR , name , ) ;
OVERRIDEVAR ( return , id - > overrideval . s = * id - > storage . s , delete [ ] id - > overrideval . s , delete [ ] * id - > storage . s ) ;
* id - > storage . s = newstring ( str ) ;
if ( dofunc ) id - > changed ( ) ;
}
int getvar ( const char * name )
{
GETVAR ( id , name , 0 ) ;
return * id - > storage . i ;
}
int getvarmin ( const char * name )
{
GETVAR ( id , name , 0 ) ;
return id - > minval ;
}
int getvarmax ( const char * name )
{
GETVAR ( id , name , 0 ) ;
return id - > maxval ;
}
float getfvarmin ( const char * name )
{
_GETVAR ( id , ID_FVAR , name , 0 ) ;
return id - > minvalf ;
}
float getfvarmax ( const char * name )
{
_GETVAR ( id , ID_FVAR , name , 0 ) ;
return id - > maxvalf ;
}
ICOMMAND ( getvarmin , " s " , ( char * s ) , intret ( getvarmin ( s ) ) ) ;
ICOMMAND ( getvarmax , " s " , ( char * s ) , intret ( getvarmax ( s ) ) ) ;
ICOMMAND ( getfvarmin , " s " , ( char * s ) , floatret ( getfvarmin ( s ) ) ) ;
ICOMMAND ( getfvarmax , " s " , ( char * s ) , floatret ( getfvarmax ( s ) ) ) ;
2020-07-30 03:15:44 +02:00
bool identexists ( const char * name ) { return idents . access ( name ) ! = nullptr ; }
2020-04-15 18:39:17 +02:00
ICOMMAND ( identexists , " s " , ( char * s ) , intret ( identexists ( s ) ? 1 : 0 ) ) ;
ident * getident ( const char * name ) { return idents . access ( name ) ; }
void touchvar ( const char * name )
{
ident * id = idents . access ( name ) ;
if ( id ) switch ( id - > type )
{
case ID_VAR :
case ID_FVAR :
case ID_SVAR :
id - > changed ( ) ;
break ;
}
}
const char * getalias ( const char * name )
{
ident * i = idents . access ( name ) ;
return i & & i - > type = = ID_ALIAS & & ( i - > index > = MAXARGS | | aliasstack - > usedargs & ( 1 < < i - > index ) ) ? i - > getstr ( ) : " " ;
}
ICOMMAND ( getalias , " s " , ( char * s ) , result ( getalias ( s ) ) ) ;
int clampvar ( ident * id , int val , int minval , int maxval )
{
if ( val < minval ) val = minval ;
else if ( val > maxval ) val = maxval ;
else return val ;
debugcode ( id - > flags & IDF_HEX ?
( minval < = 255 ? " valid range for %s is %d..0x%X " : " valid range for %s is 0x%X..0x%X " ) :
" valid range for %s is %d..%d " ,
id - > name , minval , maxval ) ;
return val ;
}
void setvarchecked ( ident * id , int val )
{
if ( id - > flags & IDF_READONLY ) debugcode ( " variable %s is read-only " , id - > name ) ;
# ifndef STANDALONE
else if ( ! ( id - > flags & IDF_OVERRIDE ) | | identflags & IDF_OVERRIDDEN | | game : : allowedittoggle ( ) )
# else
else
# endif
{
OVERRIDEVAR ( return , id - > overrideval . i = * id - > storage . i , , )
if ( val < id - > minval | | val > id - > maxval ) val = clampvar ( id , val , id - > minval , id - > maxval ) ;
* id - > storage . i = val ;
id - > changed ( ) ; // call trigger function if available
# ifndef STANDALONE
if ( id - > flags & IDF_OVERRIDE & & ! ( identflags & IDF_OVERRIDDEN ) ) game : : vartrigger ( id ) ;
# endif
}
}
static inline void setvarchecked ( ident * id , tagval * args , int numargs )
{
int val = forceint ( args [ 0 ] ) ;
if ( id - > flags & IDF_HEX & & numargs > 1 )
{
val = ( val < < 16 ) | ( forceint ( args [ 1 ] ) < < 8 ) ;
if ( numargs > 2 ) val | = forceint ( args [ 2 ] ) ;
}
setvarchecked ( id , val ) ;
}
float clampfvar ( ident * id , float val , float minval , float maxval )
{
if ( val < minval ) val = minval ;
else if ( val > maxval ) val = maxval ;
else return val ;
debugcode ( " valid range for %s is %s..%s " , id - > name , floatstr ( minval ) , floatstr ( maxval ) ) ;
return val ;
}
void setfvarchecked ( ident * id , float val )
{
if ( id - > flags & IDF_READONLY ) debugcode ( " variable %s is read-only " , id - > name ) ;
# ifndef STANDALONE
else if ( ! ( id - > flags & IDF_OVERRIDE ) | | identflags & IDF_OVERRIDDEN | | game : : allowedittoggle ( ) )
# else
else
# endif
{
OVERRIDEVAR ( return , id - > overrideval . f = * id - > storage . f , , ) ;
if ( val < id - > minvalf | | val > id - > maxvalf ) val = clampfvar ( id , val , id - > minvalf , id - > maxvalf ) ;
* id - > storage . f = val ;
id - > changed ( ) ;
# ifndef STANDALONE
if ( id - > flags & IDF_OVERRIDE & & ! ( identflags & IDF_OVERRIDDEN ) ) game : : vartrigger ( id ) ;
# endif
}
}
void setsvarchecked ( ident * id , const char * val )
{
if ( id - > flags & IDF_READONLY ) debugcode ( " variable %s is read-only " , id - > name ) ;
# ifndef STANDALONE
else if ( ! ( id - > flags & IDF_OVERRIDE ) | | identflags & IDF_OVERRIDDEN | | game : : allowedittoggle ( ) )
# else
else
# endif
{
OVERRIDEVAR ( return , id - > overrideval . s = * id - > storage . s , delete [ ] id - > overrideval . s , delete [ ] * id - > storage . s ) ;
* id - > storage . s = newstring ( val ) ;
id - > changed ( ) ;
# ifndef STANDALONE
if ( id - > flags & IDF_OVERRIDE & & ! ( identflags & IDF_OVERRIDDEN ) ) game : : vartrigger ( id ) ;
# endif
}
}
bool addcommand ( const char * name , identfun fun , const char * args , int type )
{
uint argmask = 0 ;
int numargs = 0 ;
bool limit = true ;
if ( args ) for ( const char * fmt = args ; * fmt ; fmt + + ) switch ( * fmt )
{
case ' i ' : case ' b ' : case ' f ' : case ' F ' : case ' t ' : case ' T ' : case ' E ' : case ' N ' : case ' D ' : if ( numargs < MAXARGS ) numargs + + ; break ;
case ' S ' : case ' s ' : case ' e ' : case ' r ' : case ' $ ' : if ( numargs < MAXARGS ) { argmask | = 1 < < numargs ; numargs + + ; } break ;
case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' : if ( numargs < MAXARGS ) fmt - = * fmt - ' 0 ' + 1 ; break ;
case ' C ' : case ' V ' : limit = false ; break ;
default : fatal ( " builtin %s declared with illegal type: %s " , name , args ) ; break ;
}
if ( limit & & numargs > MAXCOMARGS ) fatal ( " builtin %s declared with too many args: %d " , name , numargs ) ;
addident ( ident ( type , name , args , argmask , numargs , ( void * ) fun ) ) ;
return false ;
}
const char * parsestring ( const char * p )
{
for ( ; * p ; p + + ) switch ( * p )
{
case ' \r ' :
case ' \n ' :
case ' \" ' :
return p ;
case ' ^ ' :
if ( * + + p ) break ;
return p ;
}
return p ;
}
int unescapestring ( char * dst , const char * src , const char * end )
{
char * start = dst ;
while ( src < end )
{
int c = * src + + ;
if ( c = = ' ^ ' )
{
if ( src > = end ) break ;
int e = * src + + ;
switch ( e )
{
case ' n ' : * dst + + = ' \n ' ; break ;
case ' t ' : * dst + + = ' \t ' ; break ;
case ' f ' : * dst + + = ' \f ' ; break ;
default : * dst + + = e ; break ;
}
}
else * dst + + = c ;
}
* dst = ' \0 ' ;
return dst - start ;
}
2020-07-30 03:15:44 +02:00
static char * conc ( vector < char > & buf , tagval * v , int n , bool space , const char * prefix = nullptr , int prefixlen = 0 )
2020-04-15 18:39:17 +02:00
{
if ( prefix )
{
buf . put ( prefix , prefixlen ) ;
if ( space & & n ) buf . add ( ' ' ) ;
}
loopi ( n )
{
const char * s = " " ;
int len = 0 ;
switch ( v [ i ] . type )
{
case VAL_INT : s = intstr ( v [ i ] . i ) ; break ;
case VAL_FLOAT : s = floatstr ( v [ i ] . f ) ; break ;
case VAL_STR : case VAL_CSTR : s = v [ i ] . s ; break ;
case VAL_MACRO : s = v [ i ] . s ; len = v [ i ] . code [ - 1 ] > > 8 ; goto haslen ;
}
len = int ( strlen ( s ) ) ;
haslen :
buf . put ( s , len ) ;
if ( i = = n - 1 ) break ;
if ( space ) buf . add ( ' ' ) ;
}
buf . add ( ' \0 ' ) ;
return buf . getbuf ( ) ;
}
static char * conc ( tagval * v , int n , bool space , const char * prefix , int prefixlen )
{
static int vlen [ MAXARGS ] ;
static char numbuf [ 3 * MAXSTRLEN ] ;
int len = prefixlen , numlen = 0 , i = 0 ;
for ( ; i < n ; i + + ) switch ( v [ i ] . type )
{
case VAL_MACRO : len + = ( vlen [ i ] = v [ i ] . code [ - 1 ] > > 8 ) ; break ;
case VAL_STR : case VAL_CSTR : len + = ( vlen [ i ] = int ( strlen ( v [ i ] . s ) ) ) ; break ;
case VAL_INT :
if ( numlen + MAXSTRLEN > int ( sizeof ( numbuf ) ) ) goto overflow ;
intformat ( & numbuf [ numlen ] , v [ i ] . i ) ;
numlen + = ( vlen [ i ] = strlen ( & numbuf [ numlen ] ) ) ;
break ;
case VAL_FLOAT :
if ( numlen + MAXSTRLEN > int ( sizeof ( numbuf ) ) ) goto overflow ;
floatformat ( & numbuf [ numlen ] , v [ i ] . f ) ;
numlen + = ( vlen [ i ] = strlen ( & numbuf [ numlen ] ) ) ;
break ;
default : vlen [ i ] = 0 ; break ;
}
overflow :
if ( space ) len + = max ( prefix ? i : i - 1 , 0 ) ;
char * buf = newstring ( len + numlen ) ;
int offset = 0 , numoffset = 0 ;
if ( prefix )
{
memcpy ( buf , prefix , prefixlen ) ;
offset + = prefixlen ;
if ( space & & i ) buf [ offset + + ] = ' ' ;
}
loopj ( i )
{
if ( v [ j ] . type = = VAL_INT | | v [ j ] . type = = VAL_FLOAT )
{
memcpy ( & buf [ offset ] , & numbuf [ numoffset ] , vlen [ j ] ) ;
numoffset + = vlen [ j ] ;
}
else if ( vlen [ j ] ) memcpy ( & buf [ offset ] , v [ j ] . s , vlen [ j ] ) ;
offset + = vlen [ j ] ;
if ( j = = i - 1 ) break ;
if ( space ) buf [ offset + + ] = ' ' ;
}
buf [ offset ] = ' \0 ' ;
if ( i < n )
{
char * morebuf = conc ( & v [ i ] , n - i , space , buf , offset ) ;
delete [ ] buf ;
return morebuf ;
}
return buf ;
}
static inline char * conc ( tagval * v , int n , bool space )
{
2020-07-30 03:15:44 +02:00
return conc ( v , n , space , nullptr , 0 ) ;
2020-04-15 18:39:17 +02:00
}
static inline char * conc ( tagval * v , int n , bool space , const char * prefix )
{
return conc ( v , n , space , prefix , strlen ( prefix ) ) ;
}
static inline void skipcomments ( const char * & p )
{
for ( ; ; )
{
p + = strspn ( p , " \t \r " ) ;
if ( p [ 0 ] ! = ' / ' | | p [ 1 ] ! = ' / ' ) break ;
p + = strcspn ( p , " \n \0 " ) ;
}
}
static vector < char > strbuf [ 4 ] ;
static int stridx = 0 ;
static inline void cutstring ( const char * & p , stringslice & s )
{
p + + ;
const char * end = parsestring ( p ) ;
int maxlen = int ( end - p ) + 1 ;
stridx = ( stridx + 1 ) % 4 ;
vector < char > & buf = strbuf [ stridx ] ;
if ( buf . alen < maxlen ) buf . growbuf ( maxlen ) ;
s . str = buf . buf ;
s . len = unescapestring ( buf . buf , p , end ) ;
p = end ;
if ( * p = = ' \" ' ) p + + ;
}
static inline char * cutstring ( const char * & p )
{
p + + ;
const char * end = parsestring ( p ) ;
char * buf = newstring ( end - p ) ;
unescapestring ( buf , p , end ) ;
p = end ;
if ( * p = = ' \" ' ) p + + ;
return buf ;
}
static inline const char * parseword ( const char * p )
{
const int maxbrak = 100 ;
static char brakstack [ maxbrak ] ;
int brakdepth = 0 ;
for ( ; ; p + + )
{
p + = strcspn ( p , " \" /;()[] \t \r \n \0 " ) ;
switch ( p [ 0 ] )
{
case ' " ' : case ' ; ' : case ' ' : case ' \t ' : case ' \r ' : case ' \n ' : case ' \0 ' : return p ;
case ' / ' : if ( p [ 1 ] = = ' / ' ) return p ; break ;
case ' [ ' : case ' ( ' : if ( brakdepth > = maxbrak ) return p ; brakstack [ brakdepth + + ] = p [ 0 ] ; break ;
case ' ] ' : if ( brakdepth < = 0 | | brakstack [ - - brakdepth ] ! = ' [ ' ) return p ; break ;
case ' ) ' : if ( brakdepth < = 0 | | brakstack [ - - brakdepth ] ! = ' ( ' ) return p ; break ;
}
}
return p ;
}
static inline void cutword ( const char * & p , stringslice & s )
{
s . str = p ;
p = parseword ( p ) ;
s . len = int ( p - s . str ) ;
}
static inline char * cutword ( const char * & p )
{
const char * word = p ;
p = parseword ( p ) ;
2020-07-30 03:15:44 +02:00
return p ! = word ? newstring ( word , p - word ) : nullptr ;
2020-04-15 18:39:17 +02:00
}
# define retcode(type, defaultret) ((type) >= VAL_ANY ? ((type) == VAL_CSTR ? RET_STR : (defaultret)) : (type) << CODE_RET)
# define retcodeint(type) retcode(type, RET_INT)
# define retcodefloat(type) retcode(type, RET_FLOAT)
# define retcodeany(type) retcode(type, 0)
# define retcodestr(type) ((type) >= VAL_ANY ? RET_STR : (type) << CODE_RET)
static inline void compilestr ( vector < uint > & code , const char * word , int len , bool macro = false )
{
if ( len < = 3 & & ! macro )
{
uint op = CODE_VALI | RET_STR ;
loopi ( len ) op | = uint ( uchar ( word [ i ] ) ) < < ( ( i + 1 ) * 8 ) ;
code . add ( op ) ;
return ;
}
code . add ( ( macro ? CODE_MACRO : CODE_VAL | RET_STR ) | ( len < < 8 ) ) ;
code . put ( ( const uint * ) word , len / sizeof ( uint ) ) ;
size_t endlen = len % sizeof ( uint ) ;
union
{
char c [ sizeof ( uint ) ] ;
uint u ;
} end ;
end . u = 0 ;
memcpy ( end . c , word + len - endlen , endlen ) ;
code . add ( end . u ) ;
}
static inline void compilestr ( vector < uint > & code ) { code . add ( CODE_VALI | RET_STR ) ; }
static inline void compilestr ( vector < uint > & code , const stringslice & word , bool macro = false ) { compilestr ( code , word . str , word . len , macro ) ; }
static inline void compilestr ( vector < uint > & code , const char * word , bool macro = false ) { compilestr ( code , word , int ( strlen ( word ) ) , macro ) ; }
static inline void compileunescapestring ( vector < uint > & code , const char * & p , bool macro = false )
{
p + + ;
const char * end = parsestring ( p ) ;
code . add ( macro ? CODE_MACRO : CODE_VAL | RET_STR ) ;
char * buf = ( char * ) code . reserve ( int ( end - p ) / sizeof ( uint ) + 1 ) . buf ;
int len = unescapestring ( buf , p , end ) ;
memset ( & buf [ len ] , 0 , sizeof ( uint ) - len % sizeof ( uint ) ) ;
code . last ( ) | = len < < 8 ;
code . advance ( len / sizeof ( uint ) + 1 ) ;
p = end ;
if ( * p = = ' \" ' ) p + + ;
}
static inline void compileint ( vector < uint > & code , int i = 0 )
{
if ( i > = - 0x800000 & & i < = 0x7FFFFF )
code . add ( CODE_VALI | RET_INT | ( i < < 8 ) ) ;
else
{
code . add ( CODE_VAL | RET_INT ) ;
code . add ( i ) ;
}
}
static inline void compilenull ( vector < uint > & code )
{
code . add ( CODE_VALI | RET_NULL ) ;
}
static uint emptyblock [ VAL_ANY ] [ 2 ] =
{
{ CODE_START + 0x100 , CODE_EXIT | RET_NULL } ,
{ CODE_START + 0x100 , CODE_EXIT | RET_INT } ,
{ CODE_START + 0x100 , CODE_EXIT | RET_FLOAT } ,
{ CODE_START + 0x100 , CODE_EXIT | RET_STR }
} ;
static inline void compileblock ( vector < uint > & code )
{
code . add ( CODE_EMPTY ) ;
}
static void compilestatements ( vector < uint > & code , const char * & p , int rettype , int brak = ' \0 ' , int prevargs = 0 ) ;
static inline const char * compileblock ( vector < uint > & code , const char * p , int rettype = RET_NULL , int brak = ' \0 ' )
{
int start = code . length ( ) ;
code . add ( CODE_BLOCK ) ;
code . add ( CODE_OFFSET | ( ( start + 2 ) < < 8 ) ) ;
if ( p ) compilestatements ( code , p , VAL_ANY , brak ) ;
if ( code . length ( ) > start + 2 )
{
code . add ( CODE_EXIT | rettype ) ;
code [ start ] | = uint ( code . length ( ) - ( start + 1 ) ) < < 8 ;
}
else
{
code . setsize ( start ) ;
code . add ( CODE_EMPTY | rettype ) ;
}
return p ;
}
static inline void compileident ( vector < uint > & code , ident * id = dummyident )
{
code . add ( ( id - > index < MAXARGS ? CODE_IDENTARG : CODE_IDENT ) | ( id - > index < < 8 ) ) ;
}
static inline void compileident ( vector < uint > & code , const stringslice & word )
{
compileident ( code , newident ( word , IDF_UNKNOWN ) ) ;
}
static inline void compileint ( vector < uint > & code , const stringslice & word )
{
compileint ( code , word . len ? parseint ( word . str ) : 0 ) ;
}
static inline void compilefloat ( vector < uint > & code , float f = 0.0f )
{
if ( int ( f ) = = f & & f > = - 0x800000 & & f < = 0x7FFFFF )
code . add ( CODE_VALI | RET_FLOAT | ( int ( f ) < < 8 ) ) ;
else
{
union { float f ; uint u ; } conv ;
conv . f = f ;
code . add ( CODE_VAL | RET_FLOAT ) ;
code . add ( conv . u ) ;
}
}
static inline void compilefloat ( vector < uint > & code , const stringslice & word )
{
compilefloat ( code , word . len ? parsefloat ( word . str ) : 0.0f ) ;
}
static inline bool getbool ( const char * s )
{
switch ( s [ 0 ] )
{
case ' + ' : case ' - ' :
switch ( s [ 1 ] )
{
case ' 0 ' : break ;
case ' . ' : return ! isdigit ( s [ 2 ] ) | | parsefloat ( s ) ! = 0 ;
default : return true ;
}
// fall-through
case ' 0 ' :
{
char * end ;
int val = int ( strtoul ( ( char * ) s , & end , 0 ) ) ;
if ( val ) return true ;
switch ( * end )
{
case ' e ' : case ' . ' : return parsefloat ( s ) ! = 0 ;
default : return false ;
}
}
case ' . ' : return ! isdigit ( s [ 1 ] ) | | parsefloat ( s ) ! = 0 ;
case ' \0 ' : return false ;
default : return true ;
}
}
static inline bool getbool ( const tagval & v )
{
switch ( v . type )
{
case VAL_FLOAT : return v . f ! = 0 ;
case VAL_INT : return v . i ! = 0 ;
case VAL_STR : case VAL_MACRO : case VAL_CSTR : return getbool ( v . s ) ;
default : return false ;
}
}
2020-07-30 03:15:44 +02:00
static inline void compileval ( vector < uint > & code , int wordtype , const stringslice & word = stringslice ( nullptr , 0 ) )
2020-04-15 18:39:17 +02:00
{
switch ( wordtype )
{
case VAL_CANY : if ( word . len ) compilestr ( code , word , true ) ; else compilenull ( code ) ; break ;
case VAL_CSTR : compilestr ( code , word , true ) ; break ;
case VAL_ANY : if ( word . len ) compilestr ( code , word ) ; else compilenull ( code ) ; break ;
case VAL_STR : compilestr ( code , word ) ; break ;
case VAL_FLOAT : compilefloat ( code , word ) ; break ;
case VAL_INT : compileint ( code , word ) ; break ;
case VAL_COND : if ( word . len ) compileblock ( code , word . str ) ; else compilenull ( code ) ; break ;
case VAL_CODE : compileblock ( code , word . str ) ; break ;
case VAL_IDENT : compileident ( code , word ) ; break ;
default : break ;
}
}
2020-07-30 03:15:44 +02:00
static stringslice unusedword ( nullptr , 0 ) ;
2020-04-15 18:39:17 +02:00
static bool compilearg ( vector < uint > & code , const char * & p , int wordtype , int prevargs = MAXRESULTS , stringslice & word = unusedword ) ;
static void compilelookup ( vector < uint > & code , const char * & p , int ltype , int prevargs = MAXRESULTS )
{
stringslice lookup ;
switch ( * + + p )
{
case ' ( ' :
case ' [ ' :
if ( ! compilearg ( code , p , VAL_CSTR , prevargs ) ) goto invalid ;
break ;
case ' $ ' :
compilelookup ( code , p , VAL_CSTR , prevargs ) ;
break ;
case ' \" ' :
cutstring ( p , lookup ) ;
goto lookupid ;
default :
{
cutword ( p , lookup ) ;
if ( ! lookup . len ) goto invalid ;
lookupid :
ident * id = newident ( lookup , IDF_UNKNOWN ) ;
if ( id ) switch ( id - > type )
{
case ID_VAR :
code . add ( CODE_IVAR | retcodeint ( ltype ) | ( id - > index < < 8 ) ) ;
switch ( ltype )
{
case VAL_POP : code . pop ( ) ; break ;
case VAL_CODE : code . add ( CODE_COMPILE ) ; break ;
case VAL_IDENT : code . add ( CODE_IDENTU ) ; break ;
}
return ;
case ID_FVAR :
code . add ( CODE_FVAR | retcodefloat ( ltype ) | ( id - > index < < 8 ) ) ;
switch ( ltype )
{
case VAL_POP : code . pop ( ) ; break ;
case VAL_CODE : code . add ( CODE_COMPILE ) ; break ;
case VAL_IDENT : code . add ( CODE_IDENTU ) ; break ;
}
return ;
case ID_SVAR :
switch ( ltype )
{
case VAL_POP : return ;
case VAL_CANY : case VAL_CSTR : case VAL_CODE : case VAL_IDENT : case VAL_COND :
code . add ( CODE_SVARM | ( id - > index < < 8 ) ) ;
break ;
default :
code . add ( CODE_SVAR | retcodestr ( ltype ) | ( id - > index < < 8 ) ) ;
break ;
}
goto done ;
case ID_ALIAS :
switch ( ltype )
{
case VAL_POP : return ;
case VAL_CANY : case VAL_COND :
code . add ( ( id - > index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM ) | ( id - > index < < 8 ) ) ;
break ;
case VAL_CSTR : case VAL_CODE : case VAL_IDENT :
code . add ( ( id - > index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM ) | RET_STR | ( id - > index < < 8 ) ) ;
break ;
default :
code . add ( ( id - > index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP ) | retcodestr ( ltype ) | ( id - > index < < 8 ) ) ;
break ;
}
goto done ;
case ID_COMMAND :
{
int comtype = CODE_COM , numargs = 0 ;
if ( prevargs > = MAXRESULTS ) code . add ( CODE_ENTER ) ;
for ( const char * fmt = id - > args ; * fmt ; fmt + + ) switch ( * fmt )
{
case ' S ' : compilestr ( code ) ; numargs + + ; break ;
2020-07-30 03:15:44 +02:00
case ' s ' : compilestr ( code , nullptr , 0 , true ) ; numargs + + ; break ;
2020-04-15 18:39:17 +02:00
case ' i ' : compileint ( code ) ; numargs + + ; break ;
case ' b ' : compileint ( code , INT_MIN ) ; numargs + + ; break ;
case ' f ' : compilefloat ( code ) ; numargs + + ; break ;
case ' F ' : code . add ( CODE_DUP | RET_FLOAT ) ; numargs + + ; break ;
case ' E ' :
case ' T ' :
case ' t ' : compilenull ( code ) ; numargs + + ; break ;
case ' e ' : compileblock ( code ) ; numargs + + ; break ;
case ' r ' : compileident ( code ) ; numargs + + ; break ;
case ' $ ' : compileident ( code , id ) ; numargs + + ; break ;
case ' N ' : compileint ( code , - 1 ) ; numargs + + ; break ;
# ifndef STANDALONE
case ' D ' : comtype = CODE_COMD ; numargs + + ; break ;
# endif
case ' C ' : comtype = CODE_COMC ; goto compilecomv ;
case ' V ' : comtype = CODE_COMV ; goto compilecomv ;
case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' : break ;
}
code . add ( comtype | retcodeany ( ltype ) | ( id - > index < < 8 ) ) ;
code . add ( ( prevargs > = MAXRESULTS ? CODE_EXIT : CODE_RESULT_ARG ) | retcodeany ( ltype ) ) ;
goto done ;
compilecomv :
code . add ( comtype | retcodeany ( ltype ) | ( numargs < < 8 ) | ( id - > index < < 13 ) ) ;
code . add ( ( prevargs > = MAXRESULTS ? CODE_EXIT : CODE_RESULT_ARG ) | retcodeany ( ltype ) ) ;
goto done ;
}
default : goto invalid ;
}
compilestr ( code , lookup , true ) ;
break ;
}
}
switch ( ltype )
{
case VAL_CANY : case VAL_COND :
code . add ( CODE_LOOKUPMU ) ;
break ;
case VAL_CSTR : case VAL_CODE : case VAL_IDENT :
code . add ( CODE_LOOKUPMU | RET_STR ) ;
break ;
default :
code . add ( CODE_LOOKUPU | retcodeany ( ltype ) ) ;
break ;
}
done :
switch ( ltype )
{
case VAL_POP : code . add ( CODE_POP ) ; break ;
case VAL_CODE : code . add ( CODE_COMPILE ) ; break ;
case VAL_COND : code . add ( CODE_COND ) ; break ;
case VAL_IDENT : code . add ( CODE_IDENTU ) ; break ;
}
return ;
invalid :
switch ( ltype )
{
case VAL_POP : break ;
case VAL_NULL : case VAL_ANY : case VAL_CANY : case VAL_WORD : case VAL_COND : compilenull ( code ) ; break ;
default : compileval ( code , ltype ) ; break ;
}
}
static bool compileblockstr ( vector < uint > & code , const char * str , const char * end , bool macro )
{
int start = code . length ( ) ;
code . add ( macro ? CODE_MACRO : CODE_VAL | RET_STR ) ;
char * buf = ( char * ) code . reserve ( ( end - str ) / sizeof ( uint ) + 1 ) . buf ;
int len = 0 ;
while ( str < end )
{
int n = strcspn ( str , " \r / \" @] \0 " ) ;
memcpy ( & buf [ len ] , str , n ) ;
len + = n ;
str + = n ;
switch ( * str )
{
case ' \r ' : str + + ; break ;
case ' \" ' :
{
const char * start = str ;
str = parsestring ( str + 1 ) ;
if ( * str = = ' \" ' ) str + + ;
memcpy ( & buf [ len ] , start , str - start ) ;
len + = str - start ;
break ;
}
case ' / ' :
if ( str [ 1 ] = = ' / ' )
{
size_t comment = strcspn ( str , " \n \0 " ) ;
if ( iscubepunct ( str [ 2 ] ) )
{
memcpy ( & buf [ len ] , str , comment ) ;
len + = comment ;
}
str + = comment ;
}
else buf [ len + + ] = * str + + ;
break ;
case ' @ ' :
case ' ] ' :
if ( str < end ) { buf [ len + + ] = * str + + ; break ; }
case ' \0 ' : goto done ;
}
}
done :
memset ( & buf [ len ] , ' \0 ' , sizeof ( uint ) - len % sizeof ( uint ) ) ;
code . advance ( len / sizeof ( uint ) + 1 ) ;
code [ start ] | = len < < 8 ;
return true ;
}
static bool compileblocksub ( vector < uint > & code , const char * & p , int prevargs )
{
stringslice lookup ;
switch ( * p )
{
case ' ( ' :
if ( ! compilearg ( code , p , VAL_CANY , prevargs ) ) return false ;
break ;
case ' [ ' :
if ( ! compilearg ( code , p , VAL_CSTR , prevargs ) ) return false ;
code . add ( CODE_LOOKUPMU ) ;
break ;
case ' \" ' :
cutstring ( p , lookup ) ;
goto lookupid ;
default :
{
lookup . str = p ;
while ( iscubealnum ( * p ) | | * p = = ' _ ' ) p + + ;
lookup . len = int ( p - lookup . str ) ;
if ( ! lookup . len ) return false ;
lookupid :
ident * id = newident ( lookup , IDF_UNKNOWN ) ;
if ( id ) switch ( id - > type )
{
case ID_VAR : code . add ( CODE_IVAR | ( id - > index < < 8 ) ) ; goto done ;
case ID_FVAR : code . add ( CODE_FVAR | ( id - > index < < 8 ) ) ; goto done ;
case ID_SVAR : code . add ( CODE_SVARM | ( id - > index < < 8 ) ) ; goto done ;
case ID_ALIAS : code . add ( ( id - > index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM ) | ( id - > index < < 8 ) ) ; goto done ;
}
compilestr ( code , lookup , true ) ;
code . add ( CODE_LOOKUPMU ) ;
done :
break ;
}
}
return true ;
}
static void compileblockmain ( vector < uint > & code , const char * & p , int wordtype , int prevargs )
{
const char * line = p , * start = p ;
int concs = 0 ;
for ( int brak = 1 ; brak ; )
{
p + = strcspn ( p , " @ \" /[] \0 " ) ;
int c = * p + + ;
switch ( c )
{
case ' \0 ' :
debugcodeline ( line , " missing \" ] \" " ) ;
p - - ;
goto done ;
case ' \" ' :
p = parsestring ( p ) ;
if ( * p = = ' \" ' ) p + + ;
break ;
case ' / ' :
if ( * p = = ' / ' ) p + = strcspn ( p , " \n \0 " ) ;
break ;
case ' [ ' : brak + + ; break ;
case ' ] ' : brak - - ; break ;
case ' @ ' :
{
const char * esc = p ;
while ( * p = = ' @ ' ) p + + ;
int level = p - ( esc - 1 ) ;
if ( brak > level ) continue ;
else if ( brak < level ) debugcodeline ( line , " too many @s " ) ;
if ( ! concs & & prevargs > = MAXRESULTS ) code . add ( CODE_ENTER ) ;
if ( concs + 2 > MAXARGS )
{
code . add ( CODE_CONCW | RET_STR | ( concs < < 8 ) ) ;
concs = 1 ;
}
if ( compileblockstr ( code , start , esc - 1 , true ) ) concs + + ;
if ( compileblocksub ( code , p , prevargs + concs ) ) concs + + ;
if ( concs ) start = p ;
else if ( prevargs > = MAXRESULTS ) code . pop ( ) ;
break ;
}
}
}
done :
if ( p - 1 > start )
{
if ( ! concs ) switch ( wordtype )
{
case VAL_POP :
return ;
case VAL_CODE : case VAL_COND :
p = compileblock ( code , start , RET_NULL , ' ] ' ) ;
return ;
case VAL_IDENT :
compileident ( code , stringslice ( start , p - 1 ) ) ;
return ;
}
switch ( wordtype )
{
case VAL_CSTR : case VAL_CODE : case VAL_IDENT : case VAL_CANY : case VAL_COND :
compileblockstr ( code , start , p - 1 , true ) ;
break ;
default :
compileblockstr ( code , start , p - 1 , concs > 0 ) ;
break ;
}
if ( concs > 1 ) concs + + ;
}
if ( concs )
{
if ( prevargs > = MAXRESULTS )
{
code . add ( CODE_CONCM | retcodeany ( wordtype ) | ( concs < < 8 ) ) ;
code . add ( CODE_EXIT | retcodeany ( wordtype ) ) ;
}
else code . add ( CODE_CONCW | retcodeany ( wordtype ) | ( concs < < 8 ) ) ;
}
switch ( wordtype )
{
case VAL_POP : if ( concs | | p - 1 > start ) code . add ( CODE_POP ) ; break ;
case VAL_COND : if ( ! concs & & p - 1 < = start ) compilenull ( code ) ; else code . add ( CODE_COND ) ; break ;
case VAL_CODE : if ( ! concs & & p - 1 < = start ) compileblock ( code ) ; else code . add ( CODE_COMPILE ) ; break ;
case VAL_IDENT : if ( ! concs & & p - 1 < = start ) compileident ( code ) ; else code . add ( CODE_IDENTU ) ; break ;
case VAL_CSTR : case VAL_CANY :
2020-07-30 03:15:44 +02:00
if ( ! concs & & p - 1 < = start ) compilestr ( code , nullptr , 0 , true ) ;
2020-04-15 18:39:17 +02:00
break ;
case VAL_STR : case VAL_NULL : case VAL_ANY : case VAL_WORD :
if ( ! concs & & p - 1 < = start ) compilestr ( code ) ;
break ;
default :
if ( ! concs )
{
if ( p - 1 < = start ) compileval ( code , wordtype ) ;
else code . add ( CODE_FORCE | ( wordtype < < CODE_RET ) ) ;
}
break ;
}
}
static bool compilearg ( vector < uint > & code , const char * & p , int wordtype , int prevargs , stringslice & word )
{
skipcomments ( p ) ;
switch ( * p )
{
case ' \" ' :
switch ( wordtype )
{
case VAL_POP :
p = parsestring ( p + 1 ) ;
if ( * p = = ' \" ' ) p + + ;
break ;
case VAL_COND :
{
char * s = cutstring ( p ) ;
if ( s [ 0 ] ) compileblock ( code , s ) ;
else compilenull ( code ) ;
delete [ ] s ;
break ;
}
case VAL_CODE :
{
char * s = cutstring ( p ) ;
compileblock ( code , s ) ;
delete [ ] s ;
break ;
}
case VAL_WORD :
cutstring ( p , word ) ;
break ;
case VAL_ANY :
case VAL_STR :
compileunescapestring ( code , p ) ;
break ;
case VAL_CANY :
case VAL_CSTR :
compileunescapestring ( code , p , true ) ;
break ;
default :
{
stringslice s ;
cutstring ( p , s ) ;
compileval ( code , wordtype , s ) ;
break ;
}
}
return true ;
case ' $ ' : compilelookup ( code , p , wordtype , prevargs ) ; return true ;
case ' ( ' :
p + + ;
if ( prevargs > = MAXRESULTS )
{
code . add ( CODE_ENTER ) ;
compilestatements ( code , p , wordtype > VAL_ANY ? VAL_CANY : VAL_ANY , ' ) ' ) ;
code . add ( CODE_EXIT | retcodeany ( wordtype ) ) ;
}
else
{
int start = code . length ( ) ;
compilestatements ( code , p , wordtype > VAL_ANY ? VAL_CANY : VAL_ANY , ' ) ' , prevargs ) ;
if ( code . length ( ) > start ) code . add ( CODE_RESULT_ARG | retcodeany ( wordtype ) ) ;
else { compileval ( code , wordtype ) ; return true ; }
}
switch ( wordtype )
{
case VAL_POP : code . add ( CODE_POP ) ; break ;
case VAL_COND : code . add ( CODE_COND ) ; break ;
case VAL_CODE : code . add ( CODE_COMPILE ) ; break ;
case VAL_IDENT : code . add ( CODE_IDENTU ) ; break ;
}
return true ;
case ' [ ' :
p + + ;
compileblockmain ( code , p , wordtype , prevargs ) ;
return true ;
default :
switch ( wordtype )
{
case VAL_POP :
{
const char * s = p ;
p = parseword ( p ) ;
return p ! = s ;
}
case VAL_COND :
{
char * s = cutword ( p ) ;
if ( ! s ) return false ;
compileblock ( code , s ) ;
delete [ ] s ;
return true ;
}
case VAL_CODE :
{
char * s = cutword ( p ) ;
if ( ! s ) return false ;
compileblock ( code , s ) ;
delete [ ] s ;
return true ;
}
case VAL_WORD :
cutword ( p , word ) ;
return word . len ! = 0 ;
default :
{
stringslice s ;
cutword ( p , s ) ;
if ( ! s . len ) return false ;
compileval ( code , wordtype , s ) ;
return true ;
}
}
}
}
static void compilestatements ( vector < uint > & code , const char * & p , int rettype , int brak , int prevargs )
{
const char * line = p ;
stringslice idname ;
int numargs ;
for ( ; ; )
{
skipcomments ( p ) ;
2020-07-30 03:15:44 +02:00
idname . str = nullptr ;
2020-04-15 18:39:17 +02:00
bool more = compilearg ( code , p , VAL_WORD , prevargs , idname ) ;
if ( ! more ) goto endstatement ;
skipcomments ( p ) ;
if ( p [ 0 ] = = ' = ' ) switch ( p [ 1 ] )
{
case ' / ' :
if ( p [ 2 ] ! = ' / ' ) break ;
case ' ; ' : case ' ' : case ' \t ' : case ' \r ' : case ' \n ' : case ' \0 ' :
p + + ;
if ( idname . str )
{
ident * id = newident ( idname , IDF_UNKNOWN ) ;
if ( id ) switch ( id - > type )
{
case ID_ALIAS :
if ( ! ( more = compilearg ( code , p , VAL_ANY , prevargs ) ) ) compilestr ( code ) ;
code . add ( ( id - > index < MAXARGS ? CODE_ALIASARG : CODE_ALIAS ) | ( id - > index < < 8 ) ) ;
goto endstatement ;
case ID_VAR :
if ( ! ( more = compilearg ( code , p , VAL_INT , prevargs ) ) ) compileint ( code ) ;
code . add ( CODE_IVAR1 | ( id - > index < < 8 ) ) ;
goto endstatement ;
case ID_FVAR :
if ( ! ( more = compilearg ( code , p , VAL_FLOAT , prevargs ) ) ) compilefloat ( code ) ;
code . add ( CODE_FVAR1 | ( id - > index < < 8 ) ) ;
goto endstatement ;
case ID_SVAR :
if ( ! ( more = compilearg ( code , p , VAL_CSTR , prevargs ) ) ) compilestr ( code ) ;
code . add ( CODE_SVAR1 | ( id - > index < < 8 ) ) ;
goto endstatement ;
}
compilestr ( code , idname , true ) ;
}
if ( ! ( more = compilearg ( code , p , VAL_ANY ) ) ) compilestr ( code ) ;
code . add ( CODE_ALIASU ) ;
goto endstatement ;
}
numargs = 0 ;
if ( ! idname . str )
{
noid :
while ( numargs < MAXARGS & & ( more = compilearg ( code , p , VAL_CANY , prevargs + numargs ) ) ) numargs + + ;
code . add ( CODE_CALLU | ( numargs < < 8 ) ) ;
}
else
{
ident * id = idents . access ( idname ) ;
if ( ! id )
{
if ( ! checknumber ( idname ) ) { compilestr ( code , idname , true ) ; goto noid ; }
switch ( rettype )
{
case VAL_ANY :
case VAL_CANY :
{
char * end = ( char * ) idname . str ;
int val = int ( strtoul ( idname . str , & end , 0 ) ) ;
if ( end < idname . end ( ) ) compilestr ( code , idname , rettype = = VAL_CANY ) ;
else compileint ( code , val ) ;
break ;
}
default :
compileval ( code , rettype , idname ) ;
break ;
}
code . add ( CODE_RESULT ) ;
}
else switch ( id - > type )
{
case ID_ALIAS :
while ( numargs < MAXARGS & & ( more = compilearg ( code , p , VAL_ANY , prevargs + numargs ) ) ) numargs + + ;
code . add ( ( id - > index < MAXARGS ? CODE_CALLARG : CODE_CALL ) | ( numargs < < 8 ) | ( id - > index < < 13 ) ) ;
break ;
case ID_COMMAND :
{
int comtype = CODE_COM , fakeargs = 0 ;
bool rep = false ;
for ( const char * fmt = id - > args ; * fmt ; fmt + + ) switch ( * fmt )
{
case ' S ' :
case ' s ' :
if ( more ) more = compilearg ( code , p , * fmt = = ' s ' ? VAL_CSTR : VAL_STR , prevargs + numargs ) ;
if ( ! more )
{
if ( rep ) break ;
2020-07-30 03:15:44 +02:00
compilestr ( code , nullptr , 0 , * fmt = = ' s ' ) ;
2020-04-15 18:39:17 +02:00
fakeargs + + ;
}
else if ( ! fmt [ 1 ] )
{
int numconc = 1 ;
while ( numargs + numconc < MAXARGS & & ( more = compilearg ( code , p , VAL_CSTR , prevargs + numargs + numconc ) ) ) numconc + + ;
if ( numconc > 1 ) code . add ( CODE_CONC | RET_STR | ( numconc < < 8 ) ) ;
}
numargs + + ;
break ;
case ' i ' : if ( more ) more = compilearg ( code , p , VAL_INT , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; compileint ( code ) ; fakeargs + + ; } numargs + + ; break ;
case ' b ' : if ( more ) more = compilearg ( code , p , VAL_INT , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; compileint ( code , INT_MIN ) ; fakeargs + + ; } numargs + + ; break ;
case ' f ' : if ( more ) more = compilearg ( code , p , VAL_FLOAT , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; compilefloat ( code ) ; fakeargs + + ; } numargs + + ; break ;
case ' F ' : if ( more ) more = compilearg ( code , p , VAL_FLOAT , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; code . add ( CODE_DUP | RET_FLOAT ) ; fakeargs + + ; } numargs + + ; break ;
case ' T ' :
case ' t ' : if ( more ) more = compilearg ( code , p , * fmt = = ' t ' ? VAL_CANY : VAL_ANY , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; compilenull ( code ) ; fakeargs + + ; } numargs + + ; break ;
case ' E ' : if ( more ) more = compilearg ( code , p , VAL_COND , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; compilenull ( code ) ; fakeargs + + ; } numargs + + ; break ;
case ' e ' : if ( more ) more = compilearg ( code , p , VAL_CODE , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; compileblock ( code ) ; fakeargs + + ; } numargs + + ; break ;
case ' r ' : if ( more ) more = compilearg ( code , p , VAL_IDENT , prevargs + numargs ) ; if ( ! more ) { if ( rep ) break ; compileident ( code ) ; fakeargs + + ; } numargs + + ; break ;
case ' $ ' : compileident ( code , id ) ; numargs + + ; break ;
case ' N ' : compileint ( code , numargs - fakeargs ) ; numargs + + ; break ;
# ifndef STANDALONE
case ' D ' : comtype = CODE_COMD ; numargs + + ; break ;
# endif
case ' C ' : comtype = CODE_COMC ; if ( more ) while ( numargs < MAXARGS & & ( more = compilearg ( code , p , VAL_CANY , prevargs + numargs ) ) ) numargs + + ; goto compilecomv ;
case ' V ' : comtype = CODE_COMV ; if ( more ) while ( numargs < MAXARGS & & ( more = compilearg ( code , p , VAL_CANY , prevargs + numargs ) ) ) numargs + + ; goto compilecomv ;
case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' :
if ( more & & numargs < MAXARGS )
{
int numrep = * fmt - ' 0 ' + 1 ;
fmt - = numrep ;
rep = true ;
}
else for ( ; numargs > MAXARGS ; numargs - - ) code . add ( CODE_POP ) ;
break ;
}
code . add ( comtype | retcodeany ( rettype ) | ( id - > index < < 8 ) ) ;
break ;
compilecomv :
code . add ( comtype | retcodeany ( rettype ) | ( numargs < < 8 ) | ( id - > index < < 13 ) ) ;
break ;
}
case ID_LOCAL :
if ( more ) while ( numargs < MAXARGS & & ( more = compilearg ( code , p , VAL_IDENT , prevargs + numargs ) ) ) numargs + + ;
if ( more ) while ( ( more = compilearg ( code , p , VAL_POP ) ) ) ;
code . add ( CODE_LOCAL | ( numargs < < 8 ) ) ;
break ;
case ID_DO :
if ( more ) more = compilearg ( code , p , VAL_CODE , prevargs ) ;
code . add ( ( more ? CODE_DO : CODE_NULL ) | retcodeany ( rettype ) ) ;
break ;
case ID_DOARGS :
if ( more ) more = compilearg ( code , p , VAL_CODE , prevargs ) ;
code . add ( ( more ? CODE_DOARGS : CODE_NULL ) | retcodeany ( rettype ) ) ;
break ;
case ID_IF :
if ( more ) more = compilearg ( code , p , VAL_CANY , prevargs ) ;
if ( ! more ) code . add ( CODE_NULL | retcodeany ( rettype ) ) ;
else
{
int start1 = code . length ( ) ;
more = compilearg ( code , p , VAL_CODE , prevargs + 1 ) ;
if ( ! more ) { code . add ( CODE_POP ) ; code . add ( CODE_NULL | retcodeany ( rettype ) ) ; }
else
{
int start2 = code . length ( ) ;
more = compilearg ( code , p , VAL_CODE , prevargs + 2 ) ;
uint inst1 = code [ start1 ] , op1 = inst1 & ~ CODE_RET_MASK , len1 = start2 - ( start1 + 1 ) ;
if ( ! more )
{
if ( op1 = = ( CODE_BLOCK | ( len1 < < 8 ) ) )
{
code [ start1 ] = ( len1 < < 8 ) | CODE_JUMP_FALSE ;
code [ start1 + 1 ] = CODE_ENTER_RESULT ;
code [ start1 + len1 ] = ( code [ start1 + len1 ] & ~ CODE_RET_MASK ) | retcodeany ( rettype ) ;
break ;
}
compileblock ( code ) ;
}
else
{
uint inst2 = code [ start2 ] , op2 = inst2 & ~ CODE_RET_MASK , len2 = code . length ( ) - ( start2 + 1 ) ;
if ( op2 = = ( CODE_BLOCK | ( len2 < < 8 ) ) )
{
if ( op1 = = ( CODE_BLOCK | ( len1 < < 8 ) ) )
{
code [ start1 ] = ( ( start2 - start1 ) < < 8 ) | CODE_JUMP_FALSE ;
code [ start1 + 1 ] = CODE_ENTER_RESULT ;
code [ start1 + len1 ] = ( code [ start1 + len1 ] & ~ CODE_RET_MASK ) | retcodeany ( rettype ) ;
code [ start2 ] = ( len2 < < 8 ) | CODE_JUMP ;
code [ start2 + 1 ] = CODE_ENTER_RESULT ;
code [ start2 + len2 ] = ( code [ start2 + len2 ] & ~ CODE_RET_MASK ) | retcodeany ( rettype ) ;
break ;
}
else if ( op1 = = ( CODE_EMPTY | ( len1 < < 8 ) ) )
{
code [ start1 ] = CODE_NULL | ( inst2 & CODE_RET_MASK ) ;
code [ start2 ] = ( len2 < < 8 ) | CODE_JUMP_TRUE ;
code [ start2 + 1 ] = CODE_ENTER_RESULT ;
code [ start2 + len2 ] = ( code [ start2 + len2 ] & ~ CODE_RET_MASK ) | retcodeany ( rettype ) ;
break ;
}
}
}
code . add ( CODE_COM | retcodeany ( rettype ) | ( id - > index < < 8 ) ) ;
}
}
break ;
case ID_RESULT :
if ( more ) more = compilearg ( code , p , VAL_ANY , prevargs ) ;
code . add ( ( more ? CODE_RESULT : CODE_NULL ) | retcodeany ( rettype ) ) ;
break ;
case ID_NOT :
if ( more ) more = compilearg ( code , p , VAL_CANY , prevargs ) ;
code . add ( ( more ? CODE_NOT : CODE_TRUE ) | retcodeany ( rettype ) ) ;
break ;
case ID_AND :
case ID_OR :
if ( more ) more = compilearg ( code , p , VAL_COND , prevargs ) ;
if ( ! more ) { code . add ( ( id - > type = = ID_AND ? CODE_TRUE : CODE_FALSE ) | retcodeany ( rettype ) ) ; }
else
{
numargs + + ;
int start = code . length ( ) , end = start ;
while ( numargs < MAXARGS )
{
more = compilearg ( code , p , VAL_COND , prevargs + numargs ) ;
if ( ! more ) break ;
numargs + + ;
if ( ( code [ end ] & ~ CODE_RET_MASK ) ! = ( CODE_BLOCK | ( uint ( code . length ( ) - ( end + 1 ) ) < < 8 ) ) ) break ;
end = code . length ( ) ;
}
if ( more )
{
while ( numargs < MAXARGS & & ( more = compilearg ( code , p , VAL_COND , prevargs + numargs ) ) ) numargs + + ;
code . add ( CODE_COMV | retcodeany ( rettype ) | ( numargs < < 8 ) | ( id - > index < < 13 ) ) ;
}
else
{
uint op = id - > type = = ID_AND ? CODE_JUMP_RESULT_FALSE : CODE_JUMP_RESULT_TRUE ;
code . add ( op ) ;
end = code . length ( ) ;
while ( start + 1 < end )
{
uint len = code [ start ] > > 8 ;
code [ start ] = ( ( end - ( start + 1 ) ) < < 8 ) | op ;
code [ start + 1 ] = CODE_ENTER ;
code [ start + len ] = ( code [ start + len ] & ~ CODE_RET_MASK ) | retcodeany ( rettype ) ;
start + = len + 1 ;
}
}
}
break ;
case ID_VAR :
if ( ! ( more = compilearg ( code , p , VAL_INT , prevargs ) ) ) code . add ( CODE_PRINT | ( id - > index < < 8 ) ) ;
else if ( ! ( id - > flags & IDF_HEX ) | | ! ( more = compilearg ( code , p , VAL_INT , prevargs + 1 ) ) ) code . add ( CODE_IVAR1 | ( id - > index < < 8 ) ) ;
else if ( ! ( more = compilearg ( code , p , VAL_INT , prevargs + 2 ) ) ) code . add ( CODE_IVAR2 | ( id - > index < < 8 ) ) ;
else code . add ( CODE_IVAR3 | ( id - > index < < 8 ) ) ;
break ;
case ID_FVAR :
if ( ! ( more = compilearg ( code , p , VAL_FLOAT , prevargs ) ) ) code . add ( CODE_PRINT | ( id - > index < < 8 ) ) ;
else code . add ( CODE_FVAR1 | ( id - > index < < 8 ) ) ;
break ;
case ID_SVAR :
if ( ! ( more = compilearg ( code , p , VAL_CSTR , prevargs ) ) ) code . add ( CODE_PRINT | ( id - > index < < 8 ) ) ;
else
{
do + + numargs ;
while ( numargs < MAXARGS & & ( more = compilearg ( code , p , VAL_CANY , prevargs + numargs ) ) ) ;
if ( numargs > 1 ) code . add ( CODE_CONC | RET_STR | ( numargs < < 8 ) ) ;
code . add ( CODE_SVAR1 | ( id - > index < < 8 ) ) ;
}
break ;
}
}
endstatement :
if ( more ) while ( compilearg ( code , p , VAL_POP ) ) ;
p + = strcspn ( p , " )];/ \n \0 " ) ;
int c = * p + + ;
switch ( c )
{
case ' \0 ' :
if ( c ! = brak ) debugcodeline ( line , " missing \" %c \" " , brak ) ;
p - - ;
return ;
case ' ) ' :
case ' ] ' :
if ( c = = brak ) return ;
debugcodeline ( line , " unexpected \" %c \" " , c ) ;
break ;
case ' / ' :
if ( * p = = ' / ' ) p + = strcspn ( p , " \n \0 " ) ;
goto endstatement ;
}
}
}
static void compilemain ( vector < uint > & code , const char * p , int rettype = VAL_ANY )
{
code . add ( CODE_START ) ;
compilestatements ( code , p , VAL_ANY ) ;
code . add ( CODE_EXIT | ( rettype < VAL_ANY ? rettype < < CODE_RET : 0 ) ) ;
}
uint * compilecode ( const char * p )
{
vector < uint > buf ;
buf . reserve ( 64 ) ;
compilemain ( buf , p ) ;
uint * code = new uint [ buf . length ( ) ] ;
memcpy ( code , buf . getbuf ( ) , buf . length ( ) * sizeof ( uint ) ) ;
code [ 0 ] + = 0x100 ;
return code ;
}
static inline const uint * forcecode ( tagval & v )
{
if ( v . type ! = VAL_CODE )
{
vector < uint > buf ;
buf . reserve ( 64 ) ;
compilemain ( buf , v . getstr ( ) ) ;
freearg ( v ) ;
v . setcode ( buf . disown ( ) + 1 ) ;
}
return v . code ;
}
static inline void forcecond ( tagval & v )
{
switch ( v . type )
{
case VAL_STR : case VAL_MACRO : case VAL_CSTR :
if ( v . s [ 0 ] ) forcecode ( v ) ;
else v . setint ( 0 ) ;
break ;
}
}
void keepcode ( uint * code )
{
if ( ! code ) return ;
switch ( * code & CODE_OP_MASK )
{
case CODE_START :
* code + = 0x100 ;
return ;
}
switch ( code [ - 1 ] & CODE_OP_MASK )
{
case CODE_START :
code [ - 1 ] + = 0x100 ;
break ;
case CODE_OFFSET :
code - = int ( code [ - 1 ] > > 8 ) ;
* code + = 0x100 ;
break ;
}
}
void freecode ( uint * code )
{
if ( ! code ) return ;
switch ( * code & CODE_OP_MASK )
{
case CODE_START :
* code - = 0x100 ;
if ( int ( * code ) < 0x100 ) delete [ ] code ;
return ;
}
switch ( code [ - 1 ] & CODE_OP_MASK )
{
case CODE_START :
code [ - 1 ] - = 0x100 ;
if ( int ( code [ - 1 ] ) < 0x100 ) delete [ ] & code [ - 1 ] ;
break ;
case CODE_OFFSET :
code - = int ( code [ - 1 ] > > 8 ) ;
* code - = 0x100 ;
if ( int ( * code ) < 0x100 ) delete [ ] code ;
break ;
}
}
void printvar ( ident * id , int i )
{
if ( i < 0 ) conoutf ( " %s = %d " , id - > name , i ) ;
else if ( id - > flags & IDF_HEX & & id - > maxval = = 0xFFFFFF )
conoutf ( " %s = 0x%.6X (%d, %d, %d) " , id - > name , i , ( i > > 16 ) & 0xFF , ( i > > 8 ) & 0xFF , i & 0xFF ) ;
else
conoutf ( id - > flags & IDF_HEX ? " %s = 0x%X " : " %s = %d " , id - > name , i ) ;
}
void printfvar ( ident * id , float f )
{
conoutf ( " %s = %s " , id - > name , floatstr ( f ) ) ;
}
void printsvar ( ident * id , const char * s )
{
conoutf ( strchr ( s , ' " ' ) ? " %s = [%s] " : " %s = \" %s \" " , id - > name , s ) ;
}
void printvar ( ident * id )
{
switch ( id - > type )
{
case ID_VAR : printvar ( id , * id - > storage . i ) ; break ;
case ID_FVAR : printfvar ( id , * id - > storage . f ) ; break ;
case ID_SVAR : printsvar ( id , * id - > storage . s ) ; break ;
}
}
typedef void ( __cdecl * comfun ) ( ) ;
typedef void ( __cdecl * comfun1 ) ( void * ) ;
typedef void ( __cdecl * comfun2 ) ( void * , void * ) ;
typedef void ( __cdecl * comfun3 ) ( void * , void * , void * ) ;
typedef void ( __cdecl * comfun4 ) ( void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun5 ) ( void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun6 ) ( void * , void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun7 ) ( void * , void * , void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun8 ) ( void * , void * , void * , void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun9 ) ( void * , void * , void * , void * , void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun10 ) ( void * , void * , void * , void * , void * , void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun11 ) ( void * , void * , void * , void * , void * , void * , void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfun12 ) ( void * , void * , void * , void * , void * , void * , void * , void * , void * , void * , void * , void * ) ;
typedef void ( __cdecl * comfunv ) ( tagval * , int ) ;
static const uint * skipcode ( const uint * code , tagval & result = noret )
{
int depth = 0 ;
for ( ; ; )
{
uint op = * code + + ;
switch ( op & 0xFF )
{
case CODE_MACRO :
case CODE_VAL | RET_STR :
{
uint len = op > > 8 ;
code + = len / sizeof ( uint ) + 1 ;
continue ;
}
case CODE_BLOCK :
case CODE_JUMP :
case CODE_JUMP_TRUE :
case CODE_JUMP_FALSE :
case CODE_JUMP_RESULT_TRUE :
case CODE_JUMP_RESULT_FALSE :
{
uint len = op > > 8 ;
code + = len ;
continue ;
}
case CODE_ENTER :
case CODE_ENTER_RESULT :
+ + depth ;
continue ;
case CODE_EXIT | RET_NULL : case CODE_EXIT | RET_STR : case CODE_EXIT | RET_INT : case CODE_EXIT | RET_FLOAT :
if ( depth < = 0 )
{
if ( & result ! = & noret ) forcearg ( result , op & CODE_RET_MASK ) ;
return code ;
}
- - depth ;
continue ;
}
}
}
# ifndef STANDALONE
static inline uint * copycode ( const uint * src )
{
const uint * end = skipcode ( src ) ;
size_t len = end - src ;
uint * dst = new uint [ len + 1 ] ;
* dst + + = CODE_START ;
memcpy ( dst , src , len * sizeof ( uint ) ) ;
return dst ;
}
static inline void copyarg ( tagval & dst , const tagval & src )
{
switch ( src . type )
{
case VAL_INT :
case VAL_FLOAT :
case VAL_IDENT :
dst = src ;
break ;
case VAL_STR :
case VAL_MACRO :
case VAL_CSTR :
dst . setstr ( newstring ( src . s ) ) ;
break ;
case VAL_CODE :
dst . setcode ( copycode ( src . code ) ) ;
break ;
default :
dst . setnull ( ) ;
break ;
}
}
static inline void addreleaseaction ( ident * id , tagval * args , int numargs )
{
tagval * dst = addreleaseaction ( id , numargs + 1 ) ;
if ( dst ) { args [ numargs ] . setint ( 1 ) ; loopi ( numargs + 1 ) copyarg ( dst [ i ] , args [ i ] ) ; }
else args [ numargs ] . setint ( 0 ) ;
}
# endif
static inline void callcommand ( ident * id , tagval * args , int numargs , bool lookup = false )
{
int i = - 1 , fakeargs = 0 ;
bool rep = false ;
for ( const char * fmt = id - > args ; * fmt ; fmt + + ) switch ( * fmt )
{
case ' i ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setint ( 0 ) ; fakeargs + + ; } else forceint ( args [ i ] ) ; break ;
case ' b ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setint ( INT_MIN ) ; fakeargs + + ; } else forceint ( args [ i ] ) ; break ;
case ' f ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setfloat ( 0.0f ) ; fakeargs + + ; } else forcefloat ( args [ i ] ) ; break ;
case ' F ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setfloat ( args [ i - 1 ] . getfloat ( ) ) ; fakeargs + + ; } else forcefloat ( args [ i ] ) ; break ;
case ' S ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setstr ( newstring ( " " ) ) ; fakeargs + + ; } else forcestr ( args [ i ] ) ; break ;
case ' s ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setcstr ( " " ) ; fakeargs + + ; } else forcestr ( args [ i ] ) ; break ;
case ' T ' :
case ' t ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setnull ( ) ; fakeargs + + ; } break ;
case ' E ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setnull ( ) ; fakeargs + + ; } else forcecond ( args [ i ] ) ; break ;
case ' e ' :
if ( + + i > = numargs )
{
if ( rep ) break ;
args [ i ] . setcode ( emptyblock [ VAL_NULL ] + 1 ) ;
fakeargs + + ;
}
else forcecode ( args [ i ] ) ;
break ;
case ' r ' : if ( + + i > = numargs ) { if ( rep ) break ; args [ i ] . setident ( dummyident ) ; fakeargs + + ; } else forceident ( args [ i ] ) ; break ;
case ' $ ' : if ( + + i < numargs ) freearg ( args [ i ] ) ; args [ i ] . setident ( id ) ; break ;
case ' N ' : if ( + + i < numargs ) freearg ( args [ i ] ) ; args [ i ] . setint ( lookup ? - 1 : i - fakeargs ) ; break ;
# ifndef STANDALONE
case ' D ' : if ( + + i < numargs ) freearg ( args [ i ] ) ; addreleaseaction ( id , args , i ) ; fakeargs + + ; break ;
# endif
case ' C ' : { i = max ( i + 1 , numargs ) ; vector < char > buf ; ( ( comfun1 ) id - > fun ) ( conc ( buf , args , i , true ) ) ; goto cleanup ; }
case ' V ' : i = max ( i + 1 , numargs ) ; ( ( comfunv ) id - > fun ) ( args , i ) ; goto cleanup ;
case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' : if ( i + 1 < numargs ) { fmt - = * fmt - ' 0 ' + 1 ; rep = true ; } break ;
}
+ + i ;
# define OFFSETARG(n) n
# define ARG(n) (id->argmask&(1<<(n)) ? (void *)args[OFFSETARG(n)].s : (void *)&args[OFFSETARG(n)].i)
# define CALLCOM(n) \
switch ( n ) \
{ \
case 0 : ( ( comfun ) id - > fun ) ( ) ; break ; \
case 1 : ( ( comfun1 ) id - > fun ) ( ARG ( 0 ) ) ; break ; \
case 2 : ( ( comfun2 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) ) ; break ; \
case 3 : ( ( comfun3 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) ) ; break ; \
case 4 : ( ( comfun4 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) ) ; break ; \
case 5 : ( ( comfun5 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) ) ; break ; \
case 6 : ( ( comfun6 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) , ARG ( 5 ) ) ; break ; \
case 7 : ( ( comfun7 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) , ARG ( 5 ) , ARG ( 6 ) ) ; break ; \
case 8 : ( ( comfun8 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) , ARG ( 5 ) , ARG ( 6 ) , ARG ( 7 ) ) ; break ; \
case 9 : ( ( comfun9 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) , ARG ( 5 ) , ARG ( 6 ) , ARG ( 7 ) , ARG ( 8 ) ) ; break ; \
case 10 : ( ( comfun10 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) , ARG ( 5 ) , ARG ( 6 ) , ARG ( 7 ) , ARG ( 8 ) , ARG ( 9 ) ) ; break ; \
case 11 : ( ( comfun11 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) , ARG ( 5 ) , ARG ( 6 ) , ARG ( 7 ) , ARG ( 8 ) , ARG ( 9 ) , ARG ( 10 ) ) ; break ; \
case 12 : ( ( comfun12 ) id - > fun ) ( ARG ( 0 ) , ARG ( 1 ) , ARG ( 2 ) , ARG ( 3 ) , ARG ( 4 ) , ARG ( 5 ) , ARG ( 6 ) , ARG ( 7 ) , ARG ( 8 ) , ARG ( 9 ) , ARG ( 10 ) , ARG ( 11 ) ) ; break ; \
}
CALLCOM ( i )
# undef OFFSETARG
cleanup :
loopk ( i ) freearg ( args [ k ] ) ;
for ( ; i < numargs ; i + + ) freearg ( args [ i ] ) ;
}
# define MAXRUNDEPTH 255
static int rundepth = 0 ;
static const uint * runcode ( const uint * code , tagval & result )
{
result . setnull ( ) ;
if ( rundepth > = MAXRUNDEPTH )
{
debugcode ( " exceeded recursion limit " ) ;
return skipcode ( code , result ) ;
}
+ + rundepth ;
int numargs = 0 ;
tagval args [ MAXARGS + MAXRESULTS ] , * prevret = commandret ;
commandret = & result ;
for ( ; ; )
{
uint op = * code + + ;
switch ( op & 0xFF )
{
case CODE_START : case CODE_OFFSET : continue ;
# define RETOP(op, val) \
case op : \
freearg ( result ) ; \
val ; \
continue ;
RETOP ( CODE_NULL | RET_NULL , result . setnull ( ) )
RETOP ( CODE_NULL | RET_STR , result . setstr ( newstring ( " " ) ) )
RETOP ( CODE_NULL | RET_INT , result . setint ( 0 ) )
RETOP ( CODE_NULL | RET_FLOAT , result . setfloat ( 0.0f ) )
RETOP ( CODE_FALSE | RET_STR , result . setstr ( newstring ( " 0 " ) ) )
case CODE_FALSE | RET_NULL :
RETOP ( CODE_FALSE | RET_INT , result . setint ( 0 ) )
RETOP ( CODE_FALSE | RET_FLOAT , result . setfloat ( 0.0f ) )
RETOP ( CODE_TRUE | RET_STR , result . setstr ( newstring ( " 1 " ) ) )
case CODE_TRUE | RET_NULL :
RETOP ( CODE_TRUE | RET_INT , result . setint ( 1 ) )
RETOP ( CODE_TRUE | RET_FLOAT , result . setfloat ( 1.0f ) )
# define RETPOP(op, val) \
RETOP ( op , { - - numargs ; val ; freearg ( args [ numargs ] ) ; } )
RETPOP ( CODE_NOT | RET_STR , result . setstr ( newstring ( getbool ( args [ numargs ] ) ? " 0 " : " 1 " ) ) )
case CODE_NOT | RET_NULL :
RETPOP ( CODE_NOT | RET_INT , result . setint ( getbool ( args [ numargs ] ) ? 0 : 1 ) )
RETPOP ( CODE_NOT | RET_FLOAT , result . setfloat ( getbool ( args [ numargs ] ) ? 0.0f : 1.0f ) )
case CODE_POP :
freearg ( args [ - - numargs ] ) ;
continue ;
case CODE_ENTER :
code = runcode ( code , args [ numargs + + ] ) ;
continue ;
case CODE_ENTER_RESULT :
freearg ( result ) ;
code = runcode ( code , result ) ;
continue ;
case CODE_EXIT | RET_STR : case CODE_EXIT | RET_INT : case CODE_EXIT | RET_FLOAT :
forcearg ( result , op & CODE_RET_MASK ) ;
// fall-through
case CODE_EXIT | RET_NULL :
goto exit ;
case CODE_RESULT_ARG | RET_STR : case CODE_RESULT_ARG | RET_INT : case CODE_RESULT_ARG | RET_FLOAT :
forcearg ( result , op & CODE_RET_MASK ) ;
// fall-through
case CODE_RESULT_ARG | RET_NULL :
args [ numargs + + ] = result ;
result . setnull ( ) ;
continue ;
case CODE_PRINT :
printvar ( identmap [ op > > 8 ] ) ;
continue ;
case CODE_LOCAL :
{
freearg ( result ) ;
int numlocals = op > > 8 , offset = numargs - numlocals ;
identstack locals [ MAXARGS ] ;
loopi ( numlocals ) pushalias ( * args [ offset + i ] . id , locals [ i ] ) ;
code = runcode ( code , result ) ;
for ( int i = offset ; i < numargs ; i + + ) popalias ( * args [ i ] . id ) ;
goto exit ;
}
case CODE_DOARGS | RET_NULL : case CODE_DOARGS | RET_STR : case CODE_DOARGS | RET_INT : case CODE_DOARGS | RET_FLOAT :
{
UNDOARGS
freearg ( result ) ;
runcode ( args [ - - numargs ] . code , result ) ;
freearg ( args [ numargs ] ) ;
forcearg ( result , op & CODE_RET_MASK ) ;
REDOARGS
continue ;
}
case CODE_DO | RET_NULL : case CODE_DO | RET_STR : case CODE_DO | RET_INT : case CODE_DO | RET_FLOAT :
freearg ( result ) ;
runcode ( args [ - - numargs ] . code , result ) ;
freearg ( args [ numargs ] ) ;
forcearg ( result , op & CODE_RET_MASK ) ;
continue ;
case CODE_JUMP :
{
uint len = op > > 8 ;
code + = len ;
continue ;
}
case CODE_JUMP_TRUE :
{
uint len = op > > 8 ;
if ( getbool ( args [ - - numargs ] ) ) code + = len ;
freearg ( args [ numargs ] ) ;
continue ;
}
case CODE_JUMP_FALSE :
{
uint len = op > > 8 ;
if ( ! getbool ( args [ - - numargs ] ) ) code + = len ;
freearg ( args [ numargs ] ) ;
continue ;
}
case CODE_JUMP_RESULT_TRUE :
{
uint len = op > > 8 ;
freearg ( result ) ;
- - numargs ;
if ( args [ numargs ] . type = = VAL_CODE ) { runcode ( args [ numargs ] . code , result ) ; freearg ( args [ numargs ] ) ; }
else result = args [ numargs ] ;
if ( getbool ( result ) ) code + = len ;
continue ;
}
case CODE_JUMP_RESULT_FALSE :
{
uint len = op > > 8 ;
freearg ( result ) ;
- - numargs ;
if ( args [ numargs ] . type = = VAL_CODE ) { runcode ( args [ numargs ] . code , result ) ; freearg ( args [ numargs ] ) ; }
else result = args [ numargs ] ;
if ( ! getbool ( result ) ) code + = len ;
continue ;
}
case CODE_MACRO :
{
uint len = op > > 8 ;
args [ numargs + + ] . setmacro ( code ) ;
code + = len / sizeof ( uint ) + 1 ;
continue ;
}
case CODE_VAL | RET_STR :
{
uint len = op > > 8 ;
args [ numargs + + ] . setstr ( newstring ( ( const char * ) code , len ) ) ;
code + = len / sizeof ( uint ) + 1 ;
continue ;
}
case CODE_VALI | RET_STR :
{
char s [ 4 ] = { char ( ( op > > 8 ) & 0xFF ) , char ( ( op > > 16 ) & 0xFF ) , char ( ( op > > 24 ) & 0xFF ) , ' \0 ' } ;
args [ numargs + + ] . setstr ( newstring ( s ) ) ;
continue ;
}
case CODE_VAL | RET_NULL :
case CODE_VALI | RET_NULL : args [ numargs + + ] . setnull ( ) ; continue ;
case CODE_VAL | RET_INT : args [ numargs + + ] . setint ( int ( * code + + ) ) ; continue ;
case CODE_VALI | RET_INT : args [ numargs + + ] . setint ( int ( op ) > > 8 ) ; continue ;
case CODE_VAL | RET_FLOAT : args [ numargs + + ] . setfloat ( * ( const float * ) code + + ) ; continue ;
case CODE_VALI | RET_FLOAT : args [ numargs + + ] . setfloat ( float ( int ( op ) > > 8 ) ) ; continue ;
case CODE_DUP | RET_NULL : args [ numargs - 1 ] . getval ( args [ numargs ] ) ; numargs + + ; continue ;
case CODE_DUP | RET_INT : args [ numargs ] . setint ( args [ numargs - 1 ] . getint ( ) ) ; numargs + + ; continue ;
case CODE_DUP | RET_FLOAT : args [ numargs ] . setfloat ( args [ numargs - 1 ] . getfloat ( ) ) ; numargs + + ; continue ;
case CODE_DUP | RET_STR : args [ numargs ] . setstr ( newstring ( args [ numargs - 1 ] . getstr ( ) ) ) ; numargs + + ; continue ;
case CODE_FORCE | RET_STR : forcestr ( args [ numargs - 1 ] ) ; continue ;
case CODE_FORCE | RET_INT : forceint ( args [ numargs - 1 ] ) ; continue ;
case CODE_FORCE | RET_FLOAT : forcefloat ( args [ numargs - 1 ] ) ; continue ;
case CODE_RESULT | RET_NULL :
freearg ( result ) ;
result = args [ - - numargs ] ;
continue ;
case CODE_RESULT | RET_STR : case CODE_RESULT | RET_INT : case CODE_RESULT | RET_FLOAT :
freearg ( result ) ;
result = args [ - - numargs ] ;
forcearg ( result , op & CODE_RET_MASK ) ;
continue ;
case CODE_EMPTY | RET_NULL : args [ numargs + + ] . setcode ( emptyblock [ VAL_NULL ] + 1 ) ; break ;
case CODE_EMPTY | RET_STR : args [ numargs + + ] . setcode ( emptyblock [ VAL_STR ] + 1 ) ; break ;
case CODE_EMPTY | RET_INT : args [ numargs + + ] . setcode ( emptyblock [ VAL_INT ] + 1 ) ; break ;
case CODE_EMPTY | RET_FLOAT : args [ numargs + + ] . setcode ( emptyblock [ VAL_FLOAT ] + 1 ) ; break ;
case CODE_BLOCK :
{
uint len = op > > 8 ;
args [ numargs + + ] . setcode ( code + 1 ) ;
code + = len ;
continue ;
}
case CODE_COMPILE :
{
tagval & arg = args [ numargs - 1 ] ;
vector < uint > buf ;
switch ( arg . type )
{
case VAL_INT : buf . reserve ( 8 ) ; buf . add ( CODE_START ) ; compileint ( buf , arg . i ) ; buf . add ( CODE_RESULT ) ; buf . add ( CODE_EXIT ) ; break ;
case VAL_FLOAT : buf . reserve ( 8 ) ; buf . add ( CODE_START ) ; compilefloat ( buf , arg . f ) ; buf . add ( CODE_RESULT ) ; buf . add ( CODE_EXIT ) ; break ;
case VAL_STR : case VAL_MACRO : case VAL_CSTR : buf . reserve ( 64 ) ; compilemain ( buf , arg . s ) ; freearg ( arg ) ; break ;
default : buf . reserve ( 8 ) ; buf . add ( CODE_START ) ; compilenull ( buf ) ; buf . add ( CODE_RESULT ) ; buf . add ( CODE_EXIT ) ; break ;
}
arg . setcode ( buf . disown ( ) + 1 ) ;
continue ;
}
case CODE_COND :
{
tagval & arg = args [ numargs - 1 ] ;
switch ( arg . type )
{
case VAL_STR : case VAL_MACRO : case VAL_CSTR :
if ( arg . s [ 0 ] )
{
vector < uint > buf ;
buf . reserve ( 64 ) ;
compilemain ( buf , arg . s ) ;
freearg ( arg ) ;
arg . setcode ( buf . disown ( ) + 1 ) ;
}
else forcenull ( arg ) ;
break ;
}
continue ;
}
case CODE_IDENT :
args [ numargs + + ] . setident ( identmap [ op > > 8 ] ) ;
continue ;
case CODE_IDENTARG :
{
ident * id = identmap [ op > > 8 ] ;
if ( ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) )
{
pusharg ( * id , nullval , aliasstack - > argstack [ id - > index ] ) ;
aliasstack - > usedargs | = 1 < < id - > index ;
}
args [ numargs + + ] . setident ( id ) ;
continue ;
}
case CODE_IDENTU :
{
tagval & arg = args [ numargs - 1 ] ;
ident * id = arg . type = = VAL_STR | | arg . type = = VAL_MACRO | | arg . type = = VAL_CSTR ? newident ( arg . s , IDF_UNKNOWN ) : dummyident ;
if ( id - > index < MAXARGS & & ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) )
{
pusharg ( * id , nullval , aliasstack - > argstack [ id - > index ] ) ;
aliasstack - > usedargs | = 1 < < id - > index ;
}
freearg ( arg ) ;
arg . setident ( id ) ;
continue ;
}
case CODE_LOOKUPU | RET_STR :
# define LOOKUPU(aval, sval, ival, fval, nval) { \
tagval & arg = args [ numargs - 1 ] ; \
if ( arg . type ! = VAL_STR & & arg . type ! = VAL_MACRO & & arg . type ! = VAL_CSTR ) continue ; \
ident * id = idents . access ( arg . s ) ; \
if ( id ) switch ( id - > type ) \
{ \
case ID_ALIAS : \
if ( id - > flags & IDF_UNKNOWN ) break ; \
freearg ( arg ) ; \
if ( id - > index < MAXARGS & & ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) ) { nval ; continue ; } \
aval ; \
continue ; \
case ID_SVAR : freearg ( arg ) ; sval ; continue ; \
case ID_VAR : freearg ( arg ) ; ival ; continue ; \
case ID_FVAR : freearg ( arg ) ; fval ; continue ; \
case ID_COMMAND : \
{ \
freearg ( arg ) ; \
arg . setnull ( ) ; \
commandret = & arg ; \
tagval buf [ MAXARGS ] ; \
callcommand ( id , buf , 0 , true ) ; \
forcearg ( arg , op & CODE_RET_MASK ) ; \
commandret = & result ; \
continue ; \
} \
default : freearg ( arg ) ; nval ; continue ; \
} \
debugcode ( " unknown alias lookup: %s " , arg . s ) ; \
freearg ( arg ) ; \
nval ; \
continue ; \
}
LOOKUPU ( arg . setstr ( newstring ( id - > getstr ( ) ) ) ,
arg . setstr ( newstring ( * id - > storage . s ) ) ,
arg . setstr ( newstring ( intstr ( * id - > storage . i ) ) ) ,
arg . setstr ( newstring ( floatstr ( * id - > storage . f ) ) ) ,
arg . setstr ( newstring ( " " ) ) ) ;
case CODE_LOOKUP | RET_STR :
# define LOOKUP(aval) { \
ident * id = identmap [ op > > 8 ] ; \
if ( id - > flags & IDF_UNKNOWN ) debugcode ( " unknown alias lookup: %s " , id - > name ) ; \
aval ; \
continue ; \
}
LOOKUP ( args [ numargs + + ] . setstr ( newstring ( id - > getstr ( ) ) ) ) ;
case CODE_LOOKUPARG | RET_STR :
# define LOOKUPARG(aval, nval) { \
ident * id = identmap [ op > > 8 ] ; \
if ( ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) ) { nval ; continue ; } \
aval ; \
continue ; \
}
LOOKUPARG ( args [ numargs + + ] . setstr ( newstring ( id - > getstr ( ) ) ) , args [ numargs + + ] . setstr ( newstring ( " " ) ) ) ;
case CODE_LOOKUPU | RET_INT :
LOOKUPU ( arg . setint ( id - > getint ( ) ) ,
arg . setint ( parseint ( * id - > storage . s ) ) ,
arg . setint ( * id - > storage . i ) ,
arg . setint ( int ( * id - > storage . f ) ) ,
arg . setint ( 0 ) ) ;
case CODE_LOOKUP | RET_INT :
LOOKUP ( args [ numargs + + ] . setint ( id - > getint ( ) ) ) ;
case CODE_LOOKUPARG | RET_INT :
LOOKUPARG ( args [ numargs + + ] . setint ( id - > getint ( ) ) , args [ numargs + + ] . setint ( 0 ) ) ;
case CODE_LOOKUPU | RET_FLOAT :
LOOKUPU ( arg . setfloat ( id - > getfloat ( ) ) ,
arg . setfloat ( parsefloat ( * id - > storage . s ) ) ,
arg . setfloat ( float ( * id - > storage . i ) ) ,
arg . setfloat ( * id - > storage . f ) ,
arg . setfloat ( 0.0f ) ) ;
case CODE_LOOKUP | RET_FLOAT :
LOOKUP ( args [ numargs + + ] . setfloat ( id - > getfloat ( ) ) ) ;
case CODE_LOOKUPARG | RET_FLOAT :
LOOKUPARG ( args [ numargs + + ] . setfloat ( id - > getfloat ( ) ) , args [ numargs + + ] . setfloat ( 0.0f ) ) ;
case CODE_LOOKUPU | RET_NULL :
LOOKUPU ( id - > getval ( arg ) ,
arg . setstr ( newstring ( * id - > storage . s ) ) ,
arg . setint ( * id - > storage . i ) ,
arg . setfloat ( * id - > storage . f ) ,
arg . setnull ( ) ) ;
case CODE_LOOKUP | RET_NULL :
LOOKUP ( id - > getval ( args [ numargs + + ] ) ) ;
case CODE_LOOKUPARG | RET_NULL :
LOOKUPARG ( id - > getval ( args [ numargs + + ] ) , args [ numargs + + ] . setnull ( ) ) ;
case CODE_LOOKUPMU | RET_STR :
LOOKUPU ( id - > getcstr ( arg ) ,
arg . setcstr ( * id - > storage . s ) ,
arg . setstr ( newstring ( intstr ( * id - > storage . i ) ) ) ,
arg . setstr ( newstring ( floatstr ( * id - > storage . f ) ) ) ,
arg . setcstr ( " " ) ) ;
case CODE_LOOKUPM | RET_STR :
LOOKUP ( id - > getcstr ( args [ numargs + + ] ) ) ;
case CODE_LOOKUPMARG | RET_STR :
LOOKUPARG ( id - > getcstr ( args [ numargs + + ] ) , args [ numargs + + ] . setcstr ( " " ) ) ;
case CODE_LOOKUPMU | RET_NULL :
LOOKUPU ( id - > getcval ( arg ) ,
arg . setcstr ( * id - > storage . s ) ,
arg . setint ( * id - > storage . i ) ,
arg . setfloat ( * id - > storage . f ) ,
arg . setnull ( ) ) ;
case CODE_LOOKUPM | RET_NULL :
LOOKUP ( id - > getcval ( args [ numargs + + ] ) ) ;
case CODE_LOOKUPMARG | RET_NULL :
LOOKUPARG ( id - > getcval ( args [ numargs + + ] ) , args [ numargs + + ] . setnull ( ) ) ;
case CODE_SVAR | RET_STR : case CODE_SVAR | RET_NULL : args [ numargs + + ] . setstr ( newstring ( * identmap [ op > > 8 ] - > storage . s ) ) ; continue ;
case CODE_SVAR | RET_INT : args [ numargs + + ] . setint ( parseint ( * identmap [ op > > 8 ] - > storage . s ) ) ; continue ;
case CODE_SVAR | RET_FLOAT : args [ numargs + + ] . setfloat ( parsefloat ( * identmap [ op > > 8 ] - > storage . s ) ) ; continue ;
case CODE_SVARM : args [ numargs + + ] . setcstr ( * identmap [ op > > 8 ] - > storage . s ) ; continue ;
case CODE_SVAR1 : setsvarchecked ( identmap [ op > > 8 ] , args [ - - numargs ] . s ) ; freearg ( args [ numargs ] ) ; continue ;
case CODE_IVAR | RET_INT : case CODE_IVAR | RET_NULL : args [ numargs + + ] . setint ( * identmap [ op > > 8 ] - > storage . i ) ; continue ;
case CODE_IVAR | RET_STR : args [ numargs + + ] . setstr ( newstring ( intstr ( * identmap [ op > > 8 ] - > storage . i ) ) ) ; continue ;
case CODE_IVAR | RET_FLOAT : args [ numargs + + ] . setfloat ( float ( * identmap [ op > > 8 ] - > storage . i ) ) ; continue ;
case CODE_IVAR1 : setvarchecked ( identmap [ op > > 8 ] , args [ - - numargs ] . i ) ; continue ;
case CODE_IVAR2 : numargs - = 2 ; setvarchecked ( identmap [ op > > 8 ] , ( args [ numargs ] . i < < 16 ) | ( args [ numargs + 1 ] . i < < 8 ) ) ; continue ;
case CODE_IVAR3 : numargs - = 3 ; setvarchecked ( identmap [ op > > 8 ] , ( args [ numargs ] . i < < 16 ) | ( args [ numargs + 1 ] . i < < 8 ) | args [ numargs + 2 ] . i ) ; continue ;
case CODE_FVAR | RET_FLOAT : case CODE_FVAR | RET_NULL : args [ numargs + + ] . setfloat ( * identmap [ op > > 8 ] - > storage . f ) ; continue ;
case CODE_FVAR | RET_STR : args [ numargs + + ] . setstr ( newstring ( floatstr ( * identmap [ op > > 8 ] - > storage . f ) ) ) ; continue ;
case CODE_FVAR | RET_INT : args [ numargs + + ] . setint ( int ( * identmap [ op > > 8 ] - > storage . f ) ) ; continue ;
case CODE_FVAR1 : setfvarchecked ( identmap [ op > > 8 ] , args [ - - numargs ] . f ) ; continue ;
# define OFFSETARG(n) offset+n
case CODE_COM | RET_NULL : case CODE_COM | RET_STR : case CODE_COM | RET_FLOAT : case CODE_COM | RET_INT :
{
ident * id = identmap [ op > > 8 ] ;
int offset = numargs - id - > numargs ;
forcenull ( result ) ;
CALLCOM ( id - > numargs )
forcearg ( result , op & CODE_RET_MASK ) ;
freeargs ( args , numargs , offset ) ;
continue ;
}
# ifndef STANDALONE
case CODE_COMD | RET_NULL : case CODE_COMD | RET_STR : case CODE_COMD | RET_FLOAT : case CODE_COMD | RET_INT :
{
ident * id = identmap [ op > > 8 ] ;
int offset = numargs - ( id - > numargs - 1 ) ;
addreleaseaction ( id , & args [ offset ] , id - > numargs - 1 ) ;
CALLCOM ( id - > numargs )
forcearg ( result , op & CODE_RET_MASK ) ;
freeargs ( args , numargs , offset ) ;
continue ;
}
# endif
# undef OFFSETARG
case CODE_COMV | RET_NULL : case CODE_COMV | RET_STR : case CODE_COMV | RET_FLOAT : case CODE_COMV | RET_INT :
{
ident * id = identmap [ op > > 13 ] ;
int callargs = ( op > > 8 ) & 0x1F , offset = numargs - callargs ;
forcenull ( result ) ;
( ( comfunv ) id - > fun ) ( & args [ offset ] , callargs ) ;
forcearg ( result , op & CODE_RET_MASK ) ;
freeargs ( args , numargs , offset ) ;
continue ;
}
case CODE_COMC | RET_NULL : case CODE_COMC | RET_STR : case CODE_COMC | RET_FLOAT : case CODE_COMC | RET_INT :
{
ident * id = identmap [ op > > 13 ] ;
int callargs = ( op > > 8 ) & 0x1F , offset = numargs - callargs ;
forcenull ( result ) ;
{
vector < char > buf ;
buf . reserve ( MAXSTRLEN ) ;
( ( comfun1 ) id - > fun ) ( conc ( buf , & args [ offset ] , callargs , true ) ) ;
}
forcearg ( result , op & CODE_RET_MASK ) ;
freeargs ( args , numargs , offset ) ;
continue ;
}
case CODE_CONC | RET_NULL : case CODE_CONC | RET_STR : case CODE_CONC | RET_FLOAT : case CODE_CONC | RET_INT :
case CODE_CONCW | RET_NULL : case CODE_CONCW | RET_STR : case CODE_CONCW | RET_FLOAT : case CODE_CONCW | RET_INT :
{
int numconc = op > > 8 ;
char * s = conc ( & args [ numargs - numconc ] , numconc , ( op & CODE_OP_MASK ) = = CODE_CONC ) ;
freeargs ( args , numargs , numargs - numconc ) ;
args [ numargs ] . setstr ( s ) ;
forcearg ( args [ numargs ] , op & CODE_RET_MASK ) ;
numargs + + ;
continue ;
}
case CODE_CONCM | RET_NULL : case CODE_CONCM | RET_STR : case CODE_CONCM | RET_FLOAT : case CODE_CONCM | RET_INT :
{
int numconc = op > > 8 ;
char * s = conc ( & args [ numargs - numconc ] , numconc , false ) ;
freeargs ( args , numargs , numargs - numconc ) ;
result . setstr ( s ) ;
forcearg ( result , op & CODE_RET_MASK ) ;
continue ;
}
case CODE_ALIAS :
setalias ( * identmap [ op > > 8 ] , args [ - - numargs ] ) ;
continue ;
case CODE_ALIASARG :
setarg ( * identmap [ op > > 8 ] , args [ - - numargs ] ) ;
continue ;
case CODE_ALIASU :
numargs - = 2 ;
setalias ( args [ numargs ] . getstr ( ) , args [ numargs + 1 ] ) ;
freearg ( args [ numargs ] ) ;
continue ;
# define SKIPARGS(offset) offset
case CODE_CALL | RET_NULL : case CODE_CALL | RET_STR : case CODE_CALL | RET_FLOAT : case CODE_CALL | RET_INT :
{
# define FORCERESULT { \
freeargs ( args , numargs , SKIPARGS ( offset ) ) ; \
forcearg ( result , op & CODE_RET_MASK ) ; \
continue ; \
}
# define CALLALIAS { \
identstack argstack [ MAXARGS ] ; \
for ( int i = 0 ; i < callargs ; i + + ) \
pusharg ( * identmap [ i ] , args [ offset + i ] , argstack [ i ] ) ; \
int oldargs = _numargs ; \
_numargs = callargs ; \
int oldflags = identflags ; \
identflags | = id - > flags & IDF_OVERRIDDEN ; \
identlink aliaslink = { id , aliasstack , ( 1 < < callargs ) - 1 , argstack } ; \
aliasstack = & aliaslink ; \
if ( ! id - > code ) id - > code = compilecode ( id - > getstr ( ) ) ; \
uint * code = id - > code ; \
code [ 0 ] + = 0x100 ; \
runcode ( code + 1 , result ) ; \
code [ 0 ] - = 0x100 ; \
if ( int ( code [ 0 ] ) < 0x100 ) delete [ ] code ; \
aliasstack = aliaslink . next ; \
identflags = oldflags ; \
for ( int i = 0 ; i < callargs ; i + + ) \
poparg ( * identmap [ i ] ) ; \
for ( int argmask = aliaslink . usedargs & ( ~ 0U < < callargs ) , i = callargs ; argmask ; i + + ) \
if ( argmask & ( 1 < < i ) ) { poparg ( * identmap [ i ] ) ; argmask & = ~ ( 1 < < i ) ; } \
forcearg ( result , op & CODE_RET_MASK ) ; \
_numargs = oldargs ; \
numargs = SKIPARGS ( offset ) ; \
}
forcenull ( result ) ;
ident * id = identmap [ op > > 13 ] ;
int callargs = ( op > > 8 ) & 0x1F , offset = numargs - callargs ;
if ( id - > flags & IDF_UNKNOWN )
{
debugcode ( " unknown command: %s " , id - > name ) ;
FORCERESULT ;
}
CALLALIAS ;
continue ;
}
case CODE_CALLARG | RET_NULL : case CODE_CALLARG | RET_STR : case CODE_CALLARG | RET_FLOAT : case CODE_CALLARG | RET_INT :
{
forcenull ( result ) ;
ident * id = identmap [ op > > 13 ] ;
int callargs = ( op > > 8 ) & 0x1F , offset = numargs - callargs ;
if ( ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) ) FORCERESULT ;
CALLALIAS ;
continue ;
}
# undef SKIPARGS
# define SKIPARGS(offset) offset-1
case CODE_CALLU | RET_NULL : case CODE_CALLU | RET_STR : case CODE_CALLU | RET_FLOAT : case CODE_CALLU | RET_INT :
{
int callargs = op > > 8 , offset = numargs - callargs ;
tagval & idarg = args [ offset - 1 ] ;
if ( idarg . type ! = VAL_STR & & idarg . type ! = VAL_MACRO & & idarg . type ! = VAL_CSTR )
{
litval :
freearg ( result ) ;
result = idarg ;
forcearg ( result , op & CODE_RET_MASK ) ;
while ( - - numargs > = offset ) freearg ( args [ numargs ] ) ;
continue ;
}
ident * id = idents . access ( idarg . s ) ;
if ( ! id )
{
noid :
if ( checknumber ( idarg . s ) ) goto litval ;
debugcode ( " unknown command: %s " , idarg . s ) ;
forcenull ( result ) ;
FORCERESULT ;
}
forcenull ( result ) ;
switch ( id - > type )
{
default :
if ( ! id - > fun ) FORCERESULT ;
// fall-through
case ID_COMMAND :
freearg ( idarg ) ;
callcommand ( id , & args [ offset ] , callargs ) ;
forcearg ( result , op & CODE_RET_MASK ) ;
numargs = offset - 1 ;
continue ;
case ID_LOCAL :
{
identstack locals [ MAXARGS ] ;
freearg ( idarg ) ;
loopj ( callargs ) pushalias ( * forceident ( args [ offset + j ] ) , locals [ j ] ) ;
code = runcode ( code , result ) ;
loopj ( callargs ) popalias ( * args [ offset + j ] . id ) ;
goto exit ;
}
case ID_VAR :
if ( callargs < = 0 ) printvar ( id ) ; else setvarchecked ( id , & args [ offset ] , callargs ) ;
FORCERESULT ;
case ID_FVAR :
if ( callargs < = 0 ) printvar ( id ) ; else setfvarchecked ( id , forcefloat ( args [ offset ] ) ) ;
FORCERESULT ;
case ID_SVAR :
if ( callargs < = 0 ) printvar ( id ) ; else setsvarchecked ( id , forcestr ( args [ offset ] ) ) ;
FORCERESULT ;
case ID_ALIAS :
if ( id - > index < MAXARGS & & ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) ) FORCERESULT ;
if ( id - > valtype = = VAL_NULL ) goto noid ;
freearg ( idarg ) ;
CALLALIAS ;
continue ;
}
}
# undef SKIPARGS
}
}
exit :
commandret = prevret ;
- - rundepth ;
return code ;
}
void executeret ( const uint * code , tagval & result )
{
runcode ( code , result ) ;
}
void executeret ( const char * p , tagval & result )
{
vector < uint > code ;
code . reserve ( 64 ) ;
compilemain ( code , p , VAL_ANY ) ;
runcode ( code . getbuf ( ) + 1 , result ) ;
if ( int ( code [ 0 ] ) > = 0x100 ) code . disown ( ) ;
}
void executeret ( ident * id , tagval * args , int numargs , bool lookup , tagval & result )
{
result . setnull ( ) ;
+ + rundepth ;
tagval * prevret = commandret ;
commandret = & result ;
if ( rundepth > MAXRUNDEPTH ) debugcode ( " exceeded recursion limit " ) ;
else if ( id ) switch ( id - > type )
{
default :
if ( ! id - > fun ) break ;
// fall-through
case ID_COMMAND :
if ( numargs < id - > numargs )
{
tagval buf [ MAXARGS ] ;
memcpy ( buf , args , numargs * sizeof ( tagval ) ) ;
callcommand ( id , buf , numargs , lookup ) ;
}
else callcommand ( id , args , numargs , lookup ) ;
numargs = 0 ;
break ;
case ID_VAR :
if ( numargs < = 0 ) printvar ( id ) ; else setvarchecked ( id , args , numargs ) ;
break ;
case ID_FVAR :
if ( numargs < = 0 ) printvar ( id ) ; else setfvarchecked ( id , forcefloat ( args [ 0 ] ) ) ;
break ;
case ID_SVAR :
if ( numargs < = 0 ) printvar ( id ) ; else setsvarchecked ( id , forcestr ( args [ 0 ] ) ) ;
break ;
case ID_ALIAS :
if ( id - > index < MAXARGS & & ! ( aliasstack - > usedargs & ( 1 < < id - > index ) ) ) break ;
if ( id - > valtype = = VAL_NULL ) break ;
# define callargs numargs
# define offset 0
# define op RET_NULL
# define SKIPARGS(offset) offset
CALLALIAS ;
# undef callargs
# undef offset
# undef op
# undef SKIPARGS
break ;
}
freeargs ( args , numargs , 0 ) ;
commandret = prevret ;
- - rundepth ;
}
char * executestr ( const uint * code )
{
tagval result ;
runcode ( code , result ) ;
2020-07-30 03:15:44 +02:00
if ( result . type = = VAL_NULL ) return nullptr ;
2020-04-15 18:39:17 +02:00
forcestr ( result ) ;
return result . s ;
}
char * executestr ( const char * p )
{
tagval result ;
executeret ( p , result ) ;
2020-07-30 03:15:44 +02:00
if ( result . type = = VAL_NULL ) return nullptr ;
2020-04-15 18:39:17 +02:00
forcestr ( result ) ;
return result . s ;
}
char * executestr ( ident * id , tagval * args , int numargs , bool lookup )
{
tagval result ;
executeret ( id , args , numargs , lookup , result ) ;
2020-07-30 03:15:44 +02:00
if ( result . type = = VAL_NULL ) return nullptr ;
2020-04-15 18:39:17 +02:00
forcestr ( result ) ;
return result . s ;
}
char * execidentstr ( const char * name , bool lookup )
{
ident * id = idents . access ( name ) ;
2020-07-30 03:15:44 +02:00
return id ? executestr ( id , nullptr , 0 , lookup ) : nullptr ;
2020-04-15 18:39:17 +02:00
}
int execute ( const uint * code )
{
tagval result ;
runcode ( code , result ) ;
int i = result . getint ( ) ;
freearg ( result ) ;
return i ;
}
int execute ( const char * p )
{
vector < uint > code ;
code . reserve ( 64 ) ;
compilemain ( code , p , VAL_INT ) ;
tagval result ;
runcode ( code . getbuf ( ) + 1 , result ) ;
if ( int ( code [ 0 ] ) > = 0x100 ) code . disown ( ) ;
int i = result . getint ( ) ;
freearg ( result ) ;
return i ;
}
int execute ( ident * id , tagval * args , int numargs , bool lookup )
{
tagval result ;
executeret ( id , args , numargs , lookup , result ) ;
int i = result . getint ( ) ;
freearg ( result ) ;
return i ;
}
int execident ( const char * name , int noid , bool lookup )
{
ident * id = idents . access ( name ) ;
2020-07-30 03:15:44 +02:00
return id ? execute ( id , nullptr , 0 , lookup ) : noid ;
2020-04-15 18:39:17 +02:00
}
float executefloat ( const uint * code )
{
tagval result ;
runcode ( code , result ) ;
float f = result . getfloat ( ) ;
freearg ( result ) ;
return f ;
}
float executefloat ( const char * p )
{
tagval result ;
executeret ( p , result ) ;
float f = result . getfloat ( ) ;
freearg ( result ) ;
return f ;
}
float executefloat ( ident * id , tagval * args , int numargs , bool lookup )
{
tagval result ;
executeret ( id , args , numargs , lookup , result ) ;
float f = result . getfloat ( ) ;
freearg ( result ) ;
return f ;
}
float execidentfloat ( const char * name , float noid , bool lookup )
{
ident * id = idents . access ( name ) ;
2020-07-30 03:15:44 +02:00
return id ? executefloat ( id , nullptr , 0 , lookup ) : noid ;
2020-04-15 18:39:17 +02:00
}
bool executebool ( const uint * code )
{
tagval result ;
runcode ( code , result ) ;
bool b = getbool ( result ) ;
freearg ( result ) ;
return b ;
}
bool executebool ( const char * p )
{
tagval result ;
executeret ( p , result ) ;
bool b = getbool ( result ) ;
freearg ( result ) ;
return b ;
}
bool executebool ( ident * id , tagval * args , int numargs , bool lookup )
{
tagval result ;
executeret ( id , args , numargs , lookup , result ) ;
bool b = getbool ( result ) ;
freearg ( result ) ;
return b ;
}
bool execidentbool ( const char * name , bool noid , bool lookup )
{
ident * id = idents . access ( name ) ;
2020-07-30 03:15:44 +02:00
return id ? executebool ( id , nullptr , 0 , lookup ) : noid ;
2020-04-15 18:39:17 +02:00
}
bool execfile ( const char * cfgfile , bool msg )
{
string s ;
copystring ( s , cfgfile ) ;
2020-07-30 03:15:44 +02:00
char * buf = loadfile ( path ( s ) , nullptr ) ;
2020-04-15 18:39:17 +02:00
if ( ! buf )
{
if ( msg ) conoutf ( CON_ERROR , " could not read \" %s \" " , cfgfile ) ;
return false ;
}
const char * oldsourcefile = sourcefile , * oldsourcestr = sourcestr ;
sourcefile = cfgfile ;
sourcestr = buf ;
execute ( buf ) ;
sourcefile = oldsourcefile ;
sourcestr = oldsourcestr ;
delete [ ] buf ;
return true ;
}
ICOMMAND ( exec , " sb " , ( char * file , int * msg ) , intret ( execfile ( file , * msg ! = 0 ) ? 1 : 0 ) ) ;
const char * escapestring ( const char * s )
{
stridx = ( stridx + 1 ) % 4 ;
vector < char > & buf = strbuf [ stridx ] ;
buf . setsize ( 0 ) ;
buf . add ( ' " ' ) ;
for ( ; * s ; s + + ) switch ( * s )
{
case ' \n ' : buf . put ( " ^n " , 2 ) ; break ;
case ' \t ' : buf . put ( " ^t " , 2 ) ; break ;
case ' \f ' : buf . put ( " ^f " , 2 ) ; break ;
case ' " ' : buf . put ( " ^ \" " , 2 ) ; break ;
case ' ^ ' : buf . put ( " ^^ " , 2 ) ; break ;
default : buf . add ( * s ) ; break ;
}
buf . put ( " \" \0 " , 2 ) ;
return buf . getbuf ( ) ;
}
ICOMMAND ( escape , " s " , ( char * s ) , result ( escapestring ( s ) ) ) ;
ICOMMAND ( unescape , " s " , ( char * s ) ,
{
int len = strlen ( s ) ;
char * d = newstring ( len ) ;
unescapestring ( d , s , & s [ len ] ) ;
stringret ( d ) ;
} ) ;
const char * escapeid ( const char * s )
{
const char * end = s + strcspn ( s , " \" /;()[]@ \f \t \r \n \0 " ) ;
return * end ? escapestring ( s ) : s ;
}
bool validateblock ( const char * s )
{
const int maxbrak = 100 ;
static char brakstack [ maxbrak ] ;
int brakdepth = 0 ;
for ( ; * s ; s + + ) switch ( * s )
{
case ' [ ' : case ' ( ' : if ( brakdepth > = maxbrak ) return false ; brakstack [ brakdepth + + ] = * s ; break ;
case ' ] ' : if ( brakdepth < = 0 | | brakstack [ - - brakdepth ] ! = ' [ ' ) return false ; break ;
case ' ) ' : if ( brakdepth < = 0 | | brakstack [ - - brakdepth ] ! = ' ( ' ) return false ; break ;
case ' " ' : s = parsestring ( s + 1 ) ; if ( * s ! = ' " ' ) return false ; break ;
case ' / ' : if ( s [ 1 ] = = ' / ' ) return false ; break ;
case ' @ ' : case ' \f ' : return false ;
}
return brakdepth = = 0 ;
}
# ifndef STANDALONE
void writecfg ( const char * name )
{
stream * f = openutf8file ( path ( name & & name [ 0 ] ? name : game : : savedconfig ( ) , true ) , " w " ) ;
if ( ! f ) return ;
f - > printf ( " // automatically written on exit, DO NOT MODIFY \n // delete this file to have %s overwrite these settings \n // modify settings in game, or put settings in %s to override anything \n \n " , game : : defaultconfig ( ) , game : : autoexec ( ) ) ;
game : : writeclientinfo ( f ) ;
f - > printf ( " \n " ) ;
vector < ident * > ids ;
enumerate ( idents , ident , id , ids . add ( & id ) ) ;
ids . sortname ( ) ;
loopv ( ids )
{
ident & id = * ids [ i ] ;
if ( id . flags & IDF_PERSIST ) switch ( id . type )
{
case ID_VAR : f - > printf ( " %s %d \n " , escapeid ( id ) , * id . storage . i ) ; break ;
case ID_FVAR : f - > printf ( " %s %s \n " , escapeid ( id ) , floatstr ( * id . storage . f ) ) ; break ;
case ID_SVAR : f - > printf ( " %s %s \n " , escapeid ( id ) , escapestring ( * id . storage . s ) ) ; break ;
}
}
f - > printf ( " \n " ) ;
writebinds ( f ) ;
f - > printf ( " \n " ) ;
loopv ( ids )
{
ident & id = * ids [ i ] ;
if ( id . type = = ID_ALIAS & & id . flags & IDF_PERSIST & & ! ( id . flags & IDF_OVERRIDDEN ) ) switch ( id . valtype )
{
case VAL_STR :
if ( ! id . val . s [ 0 ] ) break ;
if ( ! validateblock ( id . val . s ) ) { f - > printf ( " %s = %s \n " , escapeid ( id ) , escapestring ( id . val . s ) ) ; break ; }
case VAL_FLOAT :
case VAL_INT :
f - > printf ( " %s = [%s] \n " , escapeid ( id ) , id . getstr ( ) ) ; break ;
}
}
f - > printf ( " \n " ) ;
writecompletions ( f ) ;
delete f ;
}
COMMAND ( writecfg , " s " ) ;
# endif
void changedvars ( )
{
vector < ident * > ids ;
enumerate ( idents , ident , id , if ( id . flags & IDF_OVERRIDDEN ) ids . add ( & id ) ) ;
ids . sortname ( ) ;
loopv ( ids ) printvar ( ids [ i ] ) ;
}
COMMAND ( changedvars , " " ) ;
// below the commands that implement a small imperative language. thanks to the semantics of
// () and [] expressions, any control construct can be defined trivially.
static string retbuf [ 4 ] ;
static int retidx = 0 ;
const char * intstr ( int v )
{
retidx = ( retidx + 1 ) % 4 ;
intformat ( retbuf [ retidx ] , v ) ;
return retbuf [ retidx ] ;
}
void intret ( int v )
{
commandret - > setint ( v ) ;
}
const char * floatstr ( float v )
{
retidx = ( retidx + 1 ) % 4 ;
floatformat ( retbuf [ retidx ] , v ) ;
return retbuf [ retidx ] ;
}
void floatret ( float v )
{
commandret - > setfloat ( v ) ;
}
const char * numberstr ( double v )
{
retidx = ( retidx + 1 ) % 4 ;
numberformat ( retbuf [ retidx ] , v ) ;
return retbuf [ retidx ] ;
}
void numberret ( double v )
{
int i = int ( v ) ;
if ( v = = i ) commandret - > setint ( i ) ;
else commandret - > setfloat ( v ) ;
}
# undef ICOMMANDNAME
# define ICOMMANDNAME(name) _stdcmd
# undef ICOMMANDSNAME
# define ICOMMANDSNAME _stdcmd
ICOMMANDK ( do , ID_DO , " e " , ( uint * body ) , executeret ( body , * commandret ) ) ;
static void doargs ( uint * body )
{
if ( aliasstack ! = & noalias )
{
UNDOARGS
executeret ( body , * commandret ) ;
REDOARGS
}
else executeret ( body , * commandret ) ;
}
COMMANDK ( doargs , ID_DOARGS , " e " ) ;
ICOMMANDK ( if , ID_IF , " tee " , ( tagval * cond , uint * t , uint * f ) , executeret ( getbool ( * cond ) ? t : f , * commandret ) ) ;
ICOMMAND ( ? , " tTT " , ( tagval * cond , tagval * t , tagval * f ) , result ( * ( getbool ( * cond ) ? t : f ) ) ) ;
ICOMMAND ( pushif , " rTe " , ( ident * id , tagval * v , uint * code ) ,
{
if ( id - > type ! = ID_ALIAS | | id - > index < MAXARGS ) return ;
if ( getbool ( * v ) )
{
identstack stack ;
pusharg ( * id , * v , stack ) ;
v - > type = VAL_NULL ;
id - > flags & = ~ IDF_UNKNOWN ;
executeret ( code , * commandret ) ;
poparg ( * id ) ;
}
} ) ;
void loopiter ( ident * id , identstack & stack , const tagval & v )
{
if ( id - > stack ! = & stack )
{
pusharg ( * id , v , stack ) ;
id - > flags & = ~ IDF_UNKNOWN ;
}
else
{
if ( id - > valtype = = VAL_STR ) delete [ ] id - > val . s ;
cleancode ( * id ) ;
id - > setval ( v ) ;
}
}
void loopend ( ident * id , identstack & stack )
{
if ( id - > stack = = & stack ) poparg ( * id ) ;
}
static inline void setiter ( ident & id , int i , identstack & stack )
{
if ( id . stack = = & stack )
{
if ( id . valtype ! = VAL_INT )
{
if ( id . valtype = = VAL_STR ) delete [ ] id . val . s ;
cleancode ( id ) ;
id . valtype = VAL_INT ;
}
id . val . i = i ;
}
else
{
tagval t ;
t . setint ( i ) ;
pusharg ( id , t , stack ) ;
id . flags & = ~ IDF_UNKNOWN ;
}
}
static inline void doloop ( ident & id , int offset , int n , int step , uint * body )
{
if ( n < = 0 | | id . type ! = ID_ALIAS ) return ;
identstack stack ;
loopi ( n )
{
setiter ( id , offset + i * step , stack ) ;
execute ( body ) ;
}
poparg ( id ) ;
}
ICOMMAND ( loop , " rie " , ( ident * id , int * n , uint * body ) , doloop ( * id , 0 , * n , 1 , body ) ) ;
ICOMMAND ( loop + , " riie " , ( ident * id , int * offset , int * n , uint * body ) , doloop ( * id , * offset , * n , 1 , body ) ) ;
ICOMMAND ( loop * , " riie " , ( ident * id , int * step , int * n , uint * body ) , doloop ( * id , 0 , * n , * step , body ) ) ;
ICOMMAND ( loop + * , " riiie " , ( ident * id , int * offset , int * step , int * n , uint * body ) , doloop ( * id , * offset , * n , * step , body ) ) ;
static inline void loopwhile ( ident & id , int offset , int n , int step , uint * cond , uint * body )
{
if ( n < = 0 | | id . type ! = ID_ALIAS ) return ;
identstack stack ;
loopi ( n )
{
setiter ( id , offset + i * step , stack ) ;
if ( ! executebool ( cond ) ) break ;
execute ( body ) ;
}
poparg ( id ) ;
}
ICOMMAND ( loopwhile , " riee " , ( ident * id , int * n , uint * cond , uint * body ) , loopwhile ( * id , 0 , * n , 1 , cond , body ) ) ;
ICOMMAND ( loopwhile + , " riiee " , ( ident * id , int * offset , int * n , uint * cond , uint * body ) , loopwhile ( * id , * offset , * n , 1 , cond , body ) ) ;
ICOMMAND ( loopwhile * , " riiee " , ( ident * id , int * step , int * n , uint * cond , uint * body ) , loopwhile ( * id , 0 , * n , * step , cond , body ) ) ;
ICOMMAND ( loopwhile + * , " riiiee " , ( ident * id , int * offset , int * step , int * n , uint * cond , uint * body ) , loopwhile ( * id , * offset , * n , * step , cond , body ) ) ;
ICOMMAND ( while , " ee " , ( uint * cond , uint * body ) , while ( executebool ( cond ) ) execute ( body ) ) ;
static inline void loopconc ( ident & id , int offset , int n , int step , uint * body , bool space )
{
if ( n < = 0 | | id . type ! = ID_ALIAS ) return ;
identstack stack ;
vector < char > s ;
loopi ( n )
{
setiter ( id , offset + i * step , stack ) ;
tagval v ;
executeret ( body , v ) ;
const char * vstr = v . getstr ( ) ;
int len = strlen ( vstr ) ;
if ( space & & i ) s . add ( ' ' ) ;
s . put ( vstr , len ) ;
freearg ( v ) ;
}
if ( n > 0 ) poparg ( id ) ;
s . add ( ' \0 ' ) ;
commandret - > setstr ( s . disown ( ) ) ;
}
ICOMMAND ( loopconcat , " rie " , ( ident * id , int * n , uint * body ) , loopconc ( * id , 0 , * n , 1 , body , true ) ) ;
ICOMMAND ( loopconcat + , " riie " , ( ident * id , int * offset , int * n , uint * body ) , loopconc ( * id , * offset , * n , 1 , body , true ) ) ;
ICOMMAND ( loopconcat * , " riie " , ( ident * id , int * step , int * n , uint * body ) , loopconc ( * id , 0 , * n , * step , body , true ) ) ;
ICOMMAND ( loopconcat + * , " riiie " , ( ident * id , int * offset , int * step , int * n , uint * body ) , loopconc ( * id , * offset , * n , * step , body , true ) ) ;
ICOMMAND ( loopconcatword , " rie " , ( ident * id , int * n , uint * body ) , loopconc ( * id , 0 , * n , 1 , body , false ) ) ;
ICOMMAND ( loopconcatword + , " riie " , ( ident * id , int * offset , int * n , uint * body ) , loopconc ( * id , * offset , * n , 1 , body , false ) ) ;
ICOMMAND ( loopconcatword * , " riie " , ( ident * id , int * step , int * n , uint * body ) , loopconc ( * id , 0 , * n , * step , body , false ) ) ;
ICOMMAND ( loopconcatword + * , " riiie " , ( ident * id , int * offset , int * step , int * n , uint * body ) , loopconc ( * id , * offset , * n , * step , body , false ) ) ;
void concat ( tagval * v , int n )
{
commandret - > setstr ( conc ( v , n , true ) ) ;
}
COMMAND ( concat , " V " ) ;
void concatword ( tagval * v , int n )
{
commandret - > setstr ( conc ( v , n , false ) ) ;
}
COMMAND ( concatword , " V " ) ;
void append ( ident * id , tagval * v , bool space )
{
if ( id - > type ! = ID_ALIAS | | v - > type = = VAL_NULL ) return ;
tagval r ;
const char * prefix = id - > getstr ( ) ;
if ( prefix [ 0 ] ) r . setstr ( conc ( v , 1 , space , prefix ) ) ;
else v - > getval ( r ) ;
if ( id - > index < MAXARGS ) setarg ( * id , r ) ; else setalias ( * id , r ) ;
}
ICOMMAND ( append , " rt " , ( ident * id , tagval * v ) , append ( id , v , true ) ) ;
ICOMMAND ( appendword , " rt " , ( ident * id , tagval * v ) , append ( id , v , false ) ) ;
void result ( tagval & v )
{
* commandret = v ;
v . type = VAL_NULL ;
}
void stringret ( char * s )
{
commandret - > setstr ( s ) ;
}
void result ( const char * s )
{
commandret - > setstr ( newstring ( s ) ) ;
}
ICOMMANDK ( result , ID_RESULT , " T " , ( tagval * v ) ,
{
* commandret = * v ;
v - > type = VAL_NULL ;
} ) ;
void format ( tagval * args , int numargs )
{
vector < char > s ;
const char * f = args [ 0 ] . getstr ( ) ;
while ( * f )
{
int c = * f + + ;
if ( c = = ' % ' )
{
int i = * f + + ;
if ( i > = ' 1 ' & & i < = ' 9 ' )
{
i - = ' 0 ' ;
const char * sub = i < numargs ? args [ i ] . getstr ( ) : " " ;
while ( * sub ) s . add ( * sub + + ) ;
}
else s . add ( i ) ;
}
else s . add ( c ) ;
}
s . add ( ' \0 ' ) ;
commandret - > setstr ( s . disown ( ) ) ;
}
COMMAND ( format , " V " ) ;
2020-07-30 03:15:44 +02:00
static const char * liststart = nullptr , * listend = nullptr , * listquotestart = nullptr , * listquoteend = nullptr ;
2020-04-15 18:39:17 +02:00
static inline void skiplist ( const char * & p )
{
for ( ; ; )
{
p + = strspn ( p , " \t \r \n " ) ;
if ( p [ 0 ] ! = ' / ' | | p [ 1 ] ! = ' / ' ) break ;
p + = strcspn ( p , " \n \0 " ) ;
}
}
static bool parselist ( const char * & s , const char * & start = liststart , const char * & end = listend , const char * & quotestart = listquotestart , const char * & quoteend = listquoteend )
{
skiplist ( s ) ;
switch ( * s )
{
case ' " ' : quotestart = s + + ; start = s ; s = parsestring ( s ) ; end = s ; if ( * s = = ' " ' ) s + + ; quoteend = s ; break ;
case ' ( ' : case ' [ ' :
quotestart = s ;
start = s + 1 ;
for ( int braktype = * s + + , brak = 1 ; ; )
{
s + = strcspn ( s , " \" /;()[] \0 " ) ;
int c = * s + + ;
switch ( c )
{
case ' \0 ' : s - - ; quoteend = end = s ; return true ;
case ' " ' : s = parsestring ( s ) ; if ( * s = = ' " ' ) s + + ; break ;
case ' / ' : if ( * s = = ' / ' ) s + = strcspn ( s , " \n \0 " ) ; break ;
case ' ( ' : case ' [ ' : if ( c = = braktype ) brak + + ; break ;
case ' ) ' : if ( braktype = = ' ( ' & & - - brak < = 0 ) goto endblock ; break ;
case ' ] ' : if ( braktype = = ' [ ' & & - - brak < = 0 ) goto endblock ; break ;
}
}
endblock :
end = s - 1 ;
quoteend = s ;
break ;
case ' \0 ' : case ' ) ' : case ' ] ' : return false ;
default : quotestart = start = s ; s = parseword ( s ) ; quoteend = end = s ; break ;
}
skiplist ( s ) ;
if ( * s = = ' ; ' ) s + + ;
return true ;
}
static inline char * listelem ( const char * start = liststart , const char * end = listend , const char * quotestart = listquotestart )
{
size_t len = end - start ;
char * s = newstring ( len ) ;
if ( * quotestart = = ' " ' ) unescapestring ( s , start , end ) ;
else { memcpy ( s , start , len ) ; s [ len ] = ' \0 ' ; }
return s ;
}
void explodelist ( const char * s , vector < char * > & elems , int limit )
{
const char * start , * end , * qstart ;
while ( ( limit < 0 | | elems . length ( ) < limit ) & & parselist ( s , start , end , qstart ) )
elems . add ( listelem ( start , end , qstart ) ) ;
}
char * indexlist ( const char * s , int pos )
{
loopi ( pos ) if ( ! parselist ( s ) ) return newstring ( " " ) ;
const char * start , * end , * qstart ;
return parselist ( s , start , end , qstart ) ? listelem ( start , end , qstart ) : newstring ( " " ) ;
}
int listlen ( const char * s )
{
int n = 0 ;
while ( parselist ( s ) ) n + + ;
return n ;
}
ICOMMAND ( listlen , " s " , ( char * s ) , intret ( listlen ( s ) ) ) ;
void at ( tagval * args , int numargs )
{
if ( ! numargs ) return ;
const char * start = args [ 0 ] . getstr ( ) , * end = start + strlen ( start ) , * qstart = " " ;
for ( int i = 1 ; i < numargs ; i + + )
{
const char * list = start ;
int pos = args [ i ] . getint ( ) ;
for ( ; pos > 0 ; pos - - ) if ( ! parselist ( list ) ) break ;
if ( pos > 0 | | ! parselist ( list , start , end , qstart ) ) start = end = qstart = " " ;
}
commandret - > setstr ( listelem ( start , end , qstart ) ) ;
}
COMMAND ( at , " si1V " ) ;
void substr ( char * s , int * start , int * count , int * numargs )
{
int len = strlen ( s ) , offset = clamp ( * start , 0 , len ) ;
commandret - > setstr ( newstring ( & s [ offset ] , * numargs > = 3 ? clamp ( * count , 0 , len - offset ) : len - offset ) ) ;
}
COMMAND ( substr , " siiN " ) ;
void sublist ( const char * s , int * skip , int * count , int * numargs )
{
int offset = max ( * skip , 0 ) , len = * numargs > = 3 ? max ( * count , 0 ) : - 1 ;
loopi ( offset ) if ( ! parselist ( s ) ) break ;
if ( len < 0 ) { if ( offset > 0 ) skiplist ( s ) ; commandret - > setstr ( newstring ( s ) ) ; return ; }
const char * list = s , * start , * end , * qstart , * qend = s ;
if ( len > 0 & & parselist ( s , start , end , list , qend ) ) while ( - - len > 0 & & parselist ( s , start , end , qstart , qend ) ) ;
commandret - > setstr ( newstring ( list , qend - list ) ) ;
}
COMMAND ( sublist , " siiN " ) ;
ICOMMAND ( stripcolors , " s " , ( char * s ) ,
{
int len = strlen ( s ) ;
char * d = newstring ( len ) ;
filtertext ( d , s , true , false , len ) ;
stringret ( d ) ;
} ) ;
static inline void setiter ( ident & id , char * val , identstack & stack )
{
if ( id . stack = = & stack )
{
if ( id . valtype = = VAL_STR ) delete [ ] id . val . s ;
else id . valtype = VAL_STR ;
cleancode ( id ) ;
id . val . s = val ;
}
else
{
tagval t ;
t . setstr ( val ) ;
pusharg ( id , t , stack ) ;
id . flags & = ~ IDF_UNKNOWN ;
}
}
void listfind ( ident * id , const char * list , const uint * body )
{
if ( id - > type ! = ID_ALIAS ) { intret ( - 1 ) ; return ; }
identstack stack ;
int n = - 1 ;
for ( const char * s = list , * start , * end ; parselist ( s , start , end ) ; )
{
+ + n ;
setiter ( * id , newstring ( start , end - start ) , stack ) ;
if ( executebool ( body ) ) { intret ( n ) ; goto found ; }
}
intret ( - 1 ) ;
found :
if ( n > = 0 ) poparg ( * id ) ;
}
COMMAND ( listfind , " rse " ) ;
void listassoc ( ident * id , const char * list , const uint * body )
{
if ( id - > type ! = ID_ALIAS ) return ;
identstack stack ;
int n = - 1 ;
for ( const char * s = list , * start , * end , * qstart ; parselist ( s , start , end ) ; )
{
+ + n ;
setiter ( * id , newstring ( start , end - start ) , stack ) ;
if ( executebool ( body ) ) { if ( parselist ( s , start , end , qstart ) ) stringret ( listelem ( start , end , qstart ) ) ; break ; }
if ( ! parselist ( s ) ) break ;
}
if ( n > = 0 ) poparg ( * id ) ;
}
COMMAND ( listassoc , " rse " ) ;
# define LISTFIND(name, fmt, type, init, cmp) \
ICOMMAND ( name , " s " fmt " i " , ( char * list , type * val , int * skip ) , \
{ \
int n = 0 ; \
init ; \
for ( const char * s = list , * start , * end , * qstart ; parselist ( s , start , end , qstart ) ; n + + ) \
{ \
if ( cmp ) { intret ( n ) ; return ; } \
loopi ( * skip ) { if ( ! parselist ( s ) ) goto notfound ; n + + ; } \
} \
notfound : \
intret ( - 1 ) ; \
} ) ;
LISTFIND ( listfind = , " i " , int , , parseint ( start ) = = * val ) ;
LISTFIND ( listfind = f , " f " , float , , parsefloat ( start ) = = * val ) ;
LISTFIND ( listfind = s , " s " , char , int len = ( int ) strlen ( val ) , int ( end - start ) = = len & & ! memcmp ( start , val , len ) ) ;
# define LISTASSOC(name, fmt, type, init, cmp) \
ICOMMAND ( name , " s " fmt , ( char * list , type * val ) , \
{ \
init ; \
for ( const char * s = list , * start , * end , * qstart ; parselist ( s , start , end ) ; ) \
{ \
if ( cmp ) { if ( parselist ( s , start , end , qstart ) ) stringret ( listelem ( start , end , qstart ) ) ; return ; } \
if ( ! parselist ( s ) ) break ; \
} \
} ) ;
LISTASSOC ( listassoc = , " i " , int , , parseint ( start ) = = * val ) ;
LISTASSOC ( listassoc = f , " f " , float , , parsefloat ( start ) = = * val ) ;
LISTASSOC ( listassoc = s , " s " , char , int len = ( int ) strlen ( val ) , int ( end - start ) = = len & & ! memcmp ( start , val , len ) ) ;
void looplist ( ident * id , const char * list , const uint * body )
{
if ( id - > type ! = ID_ALIAS ) return ;
identstack stack ;
int n = 0 ;
for ( const char * s = list , * start , * end , * qstart ; parselist ( s , start , end , qstart ) ; n + + )
{
setiter ( * id , listelem ( start , end , qstart ) , stack ) ;
execute ( body ) ;
}
if ( n ) poparg ( * id ) ;
}
COMMAND ( looplist , " rse " ) ;
void looplist2 ( ident * id , ident * id2 , const char * list , const uint * body )
{
if ( id - > type ! = ID_ALIAS | | id2 - > type ! = ID_ALIAS ) return ;
identstack stack , stack2 ;
int n = 0 ;
for ( const char * s = list , * start , * end , * qstart ; parselist ( s , start , end , qstart ) ; n + = 2 )
{
setiter ( * id , listelem ( start , end , qstart ) , stack ) ;
setiter ( * id2 , parselist ( s , start , end , qstart ) ? listelem ( start , end , qstart ) : newstring ( " " ) , stack2 ) ;
execute ( body ) ;
}
if ( n ) { poparg ( * id ) ; poparg ( * id2 ) ; }
}
COMMAND ( looplist2 , " rrse " ) ;
void looplist3 ( ident * id , ident * id2 , ident * id3 , const char * list , const uint * body )
{
if ( id - > type ! = ID_ALIAS | | id2 - > type ! = ID_ALIAS | | id3 - > type ! = ID_ALIAS ) return ;
identstack stack , stack2 , stack3 ;
int n = 0 ;
for ( const char * s = list , * start , * end , * qstart ; parselist ( s , start , end , qstart ) ; n + = 3 )
{
setiter ( * id , listelem ( start , end , qstart ) , stack ) ;
setiter ( * id2 , parselist ( s , start , end , qstart ) ? listelem ( start , end , qstart ) : newstring ( " " ) , stack2 ) ;
setiter ( * id3 , parselist ( s , start , end , qstart ) ? listelem ( start , end , qstart ) : newstring ( " " ) , stack3 ) ;
execute ( body ) ;
}
if ( n ) { poparg ( * id ) ; poparg ( * id2 ) ; poparg ( * id3 ) ; }
}
COMMAND ( looplist3 , " rrrse " ) ;
void looplistconc ( ident * id , const char * list , const uint * body , bool space )
{
if ( id - > type ! = ID_ALIAS ) return ;
identstack stack ;
vector < char > r ;
int n = 0 ;
for ( const char * s = list , * start , * end , * qstart ; parselist ( s , start , end , qstart ) ; n + + )
{
char * val = listelem ( start , end , qstart ) ;
setiter ( * id , val , stack ) ;
if ( n & & space ) r . add ( ' ' ) ;
tagval v ;
executeret ( body , v ) ;
const char * vstr = v . getstr ( ) ;
int len = strlen ( vstr ) ;
r . put ( vstr , len ) ;
freearg ( v ) ;
}
if ( n ) poparg ( * id ) ;
r . add ( ' \0 ' ) ;
commandret - > setstr ( r . disown ( ) ) ;
}
ICOMMAND ( looplistconcat , " rse " , ( ident * id , char * list , uint * body ) , looplistconc ( id , list , body , true ) ) ;
ICOMMAND ( looplistconcatword , " rse " , ( ident * id , char * list , uint * body ) , looplistconc ( id , list , body , false ) ) ;
void listfilter ( ident * id , const char * list , const uint * body )
{
if ( id - > type ! = ID_ALIAS ) return ;
identstack stack ;
vector < char > r ;
int n = 0 ;
for ( const char * s = list , * start , * end , * qstart , * qend ; parselist ( s , start , end , qstart , qend ) ; n + + )
{
char * val = newstring ( start , end - start ) ;
setiter ( * id , val , stack ) ;
if ( executebool ( body ) )
{
if ( r . length ( ) ) r . add ( ' ' ) ;
r . put ( qstart , qend - qstart ) ;
}
}
if ( n ) poparg ( * id ) ;
r . add ( ' \0 ' ) ;
commandret - > setstr ( r . disown ( ) ) ;
}
COMMAND ( listfilter , " rse " ) ;
void listcount ( ident * id , const char * list , const uint * body )
{
if ( id - > type ! = ID_ALIAS ) return ;
identstack stack ;
int n = 0 , r = 0 ;
for ( const char * s = list , * start , * end ; parselist ( s , start , end ) ; n + + )
{
char * val = newstring ( start , end - start ) ;
setiter ( * id , val , stack ) ;
if ( executebool ( body ) ) r + + ;
}
if ( n ) poparg ( * id ) ;
intret ( r ) ;
}
COMMAND ( listcount , " rse " ) ;
void prettylist ( const char * s , const char * conj )
{
vector < char > p ;
const char * start , * end , * qstart ;
for ( int len = listlen ( s ) , n = 0 ; parselist ( s , start , end , qstart ) ; n + + )
{
if ( * qstart = = ' " ' ) p . advance ( unescapestring ( p . reserve ( end - start + 1 ) . buf , start , end ) ) ;
else p . put ( start , end - start ) ;
if ( n + 1 < len )
{
if ( len > 2 | | ! conj [ 0 ] ) p . add ( ' , ' ) ;
if ( n + 2 = = len & & conj [ 0 ] )
{
p . add ( ' ' ) ;
p . put ( conj , strlen ( conj ) ) ;
}
p . add ( ' ' ) ;
}
}
p . add ( ' \0 ' ) ;
commandret - > setstr ( p . disown ( ) ) ;
}
COMMAND ( prettylist , " ss " ) ;
int listincludes ( const char * list , const char * needle , int needlelen )
{
int offset = 0 ;
for ( const char * s = list , * start , * end ; parselist ( s , start , end ) ; )
{
int len = end - start ;
if ( needlelen = = len & & ! strncmp ( needle , start , len ) ) return offset ;
offset + + ;
}
return - 1 ;
}
ICOMMAND ( indexof , " ss " , ( char * list , char * elem ) , intret ( listincludes ( list , elem , strlen ( elem ) ) ) ) ;
# define LISTMERGECMD(name, init, iter, filter, dir) \
ICOMMAND ( name , " ss " , ( const char * list , const char * elems ) , \
{ \
vector < char > p ; \
init ; \
for ( const char * start , * end , * qstart , * qend ; parselist ( iter , start , end , qstart , qend ) ; ) \
{ \
int len = end - start ; \
if ( listincludes ( filter , start , len ) dir 0 ) \
{ \
if ( ! p . empty ( ) ) p . add ( ' ' ) ; \
p . put ( qstart , qend - qstart ) ; \
} \
} \
p . add ( ' \0 ' ) ; \
commandret - > setstr ( p . disown ( ) ) ; \
} )
LISTMERGECMD ( listdel , , list , elems , < ) ;
LISTMERGECMD ( listintersect , , list , elems , > = ) ;
LISTMERGECMD ( listunion , p . put ( list , strlen ( list ) ) , elems , list , < ) ;
void listsplice ( const char * s , const char * vals , int * skip , int * count )
{
int offset = max ( * skip , 0 ) , len = max ( * count , 0 ) ;
const char * list = s , * start , * end , * qstart , * qend = s ;
loopi ( offset ) if ( ! parselist ( s , start , end , qstart , qend ) ) break ;
vector < char > p ;
if ( qend > list ) p . put ( list , qend - list ) ;
if ( * vals )
{
if ( ! p . empty ( ) ) p . add ( ' ' ) ;
p . put ( vals , strlen ( vals ) ) ;
}
loopi ( len ) if ( ! parselist ( s ) ) break ;
skiplist ( s ) ;
switch ( * s )
{
case ' \0 ' : case ' ) ' : case ' ] ' : break ;
default :
if ( ! p . empty ( ) ) p . add ( ' ' ) ;
p . put ( s , strlen ( s ) ) ;
break ;
}
p . add ( ' \0 ' ) ;
commandret - > setstr ( p . disown ( ) ) ;
}
COMMAND ( listsplice , " ssii " ) ;
ICOMMAND ( loopfiles , " rsse " , ( ident * id , char * dir , char * ext , uint * body ) ,
{
if ( id - > type ! = ID_ALIAS ) return ;
identstack stack ;
vector < char * > files ;
2020-07-30 03:15:44 +02:00
listfiles ( dir , ext [ 0 ] ? ext : nullptr , files ) ;
2020-04-15 18:39:17 +02:00
files . sort ( ) ;
files . uniquedeletearrays ( ) ;
loopv ( files )
{
setiter ( * id , files [ i ] , stack ) ;
execute ( body ) ;
}
if ( files . length ( ) ) poparg ( * id ) ;
} ) ;
void findfile_ ( char * name )
{
string fname ;
copystring ( fname , name ) ;
path ( fname ) ;
intret (
# ifndef STANDALONE
findzipfile ( fname ) | |
# endif
fileexists ( fname , " e " ) | | findfile ( fname , " e " ) ? 1 : 0
) ;
}
COMMANDN ( findfile , findfile_ , " s " ) ;
struct sortitem
{
const char * str , * quotestart , * quoteend ;
int quotelength ( ) const { return int ( quoteend - quotestart ) ; }
} ;
struct sortfun
{
ident * x , * y ;
uint * body ;
bool operator ( ) ( const sortitem & xval , const sortitem & yval )
{
if ( x - > valtype ! = VAL_CSTR ) x - > valtype = VAL_CSTR ;
cleancode ( * x ) ;
x - > val . code = ( const uint * ) xval . str ;
if ( y - > valtype ! = VAL_CSTR ) y - > valtype = VAL_CSTR ;
cleancode ( * y ) ;
y - > val . code = ( const uint * ) yval . str ;
return executebool ( body ) ;
}
} ;
void sortlist ( char * list , ident * x , ident * y , uint * body , uint * unique )
{
if ( x = = y | | x - > type ! = ID_ALIAS | | y - > type ! = ID_ALIAS ) return ;
vector < sortitem > items ;
int clen = strlen ( list ) , total = 0 ;
char * cstr = newstring ( list , clen ) ;
const char * curlist = list , * start , * end , * quotestart , * quoteend ;
while ( parselist ( curlist , start , end , quotestart , quoteend ) )
{
cstr [ end - list ] = ' \0 ' ;
sortitem item = { & cstr [ start - list ] , quotestart , quoteend } ;
items . add ( item ) ;
total + = item . quotelength ( ) ;
}
if ( items . empty ( ) )
{
commandret - > setstr ( cstr ) ;
return ;
}
identstack xstack , ystack ;
pusharg ( * x , nullval , xstack ) ; x - > flags & = ~ IDF_UNKNOWN ;
pusharg ( * y , nullval , ystack ) ; y - > flags & = ~ IDF_UNKNOWN ;
int totalunique = total , numunique = items . length ( ) ;
if ( body )
{
sortfun f = { x , y , body } ;
items . sort ( f ) ;
if ( ( * unique & CODE_OP_MASK ) ! = CODE_EXIT )
{
f . body = unique ;
totalunique = items [ 0 ] . quotelength ( ) ;
numunique = 1 ;
for ( int i = 1 ; i < items . length ( ) ; i + + )
{
sortitem & item = items [ i ] ;
2020-07-30 03:15:44 +02:00
if ( f ( items [ i - 1 ] , item ) ) item . quotestart = nullptr ;
2020-04-15 18:39:17 +02:00
else { totalunique + = item . quotelength ( ) ; numunique + + ; }
}
}
}
else
{
sortfun f = { x , y , unique } ;
totalunique = items [ 0 ] . quotelength ( ) ;
numunique = 1 ;
for ( int i = 1 ; i < items . length ( ) ; i + + )
{
sortitem & item = items [ i ] ;
loopj ( i )
{
sortitem & prev = items [ j ] ;
2020-07-30 03:15:44 +02:00
if ( prev . quotestart & & f ( item , prev ) ) { item . quotestart = nullptr ; break ; }
2020-04-15 18:39:17 +02:00
}
if ( item . quotestart ) { totalunique + = item . quotelength ( ) ; numunique + + ; }
}
}
poparg ( * x ) ;
poparg ( * y ) ;
char * sorted = cstr ;
int sortedlen = totalunique + max ( numunique - 1 , 0 ) ;
if ( clen < sortedlen )
{
delete [ ] cstr ;
sorted = newstring ( sortedlen ) ;
}
int offset = 0 ;
loopv ( items )
{
sortitem & item = items [ i ] ;
if ( ! item . quotestart ) continue ;
int len = item . quotelength ( ) ;
if ( i ) sorted [ offset + + ] = ' ' ;
memcpy ( & sorted [ offset ] , item . quotestart , len ) ;
offset + = len ;
}
sorted [ offset ] = ' \0 ' ;
commandret - > setstr ( sorted ) ;
}
COMMAND ( sortlist , " srree " ) ;
2020-07-30 03:15:44 +02:00
ICOMMAND ( uniquelist , " srre " , ( char * list , ident * x , ident * y , uint * body ) , sortlist ( list , x , y , nullptr , body ) ) ;
2020-04-15 18:39:17 +02:00
# define MATHCMD(name, fmt, type, op, initval, unaryop) \
ICOMMANDS ( name , # fmt " 1V " , ( tagval * args , int numargs ) , \
{ \
type val ; \
if ( numargs > = 2 ) \
{ \
val = args [ 0 ] . fmt ; \
type val2 = args [ 1 ] . fmt ; \
op ; \
for ( int i = 2 ; i < numargs ; i + + ) { val2 = args [ i ] . fmt ; op ; } \
} \
else { val = numargs > 0 ? args [ 0 ] . fmt : initval ; unaryop ; } \
type # # ret ( val ) ; \
} )
# define MATHICMDN(name, op, initval, unaryop) MATHCMD(#name, i, int, val = val op val2, initval, unaryop)
# define MATHICMD(name, initval, unaryop) MATHICMDN(name, name, initval, unaryop)
# define MATHFCMDN(name, op, initval, unaryop) MATHCMD(#name "f", f, float, val = val op val2, initval, unaryop)
# define MATHFCMD(name, initval, unaryop) MATHFCMDN(name, name, initval, unaryop)
# define CMPCMD(name, fmt, type, op) \
ICOMMANDS ( name , # fmt " 1V " , ( tagval * args , int numargs ) , \
{ \
bool val ; \
if ( numargs > = 2 ) \
{ \
val = args [ 0 ] . fmt op args [ 1 ] . fmt ; \
for ( int i = 2 ; i < numargs & & val ; i + + ) val = args [ i - 1 ] . fmt op args [ i ] . fmt ; \
} \
else val = ( numargs > 0 ? args [ 0 ] . fmt : 0 ) op 0 ; \
intret ( int ( val ) ) ; \
} )
# define CMPICMDN(name, op) CMPCMD(#name, i, int, op)
# define CMPICMD(name) CMPICMDN(name, name)
# define CMPFCMDN(name, op) CMPCMD(#name "f", f, float, op)
# define CMPFCMD(name) CMPFCMDN(name, name)
MATHICMD ( + , 0 , ) ;
MATHICMD ( * , 1 , ) ;
MATHICMD ( - , 0 , val = - val ) ;
CMPICMDN ( = , = = ) ;
CMPICMD ( ! = ) ;
CMPICMD ( < ) ;
CMPICMD ( > ) ;
CMPICMD ( < = ) ;
CMPICMD ( > = ) ;
MATHICMD ( ^ , 0 , val = ~ val ) ;
MATHICMDN ( ~ , ^ , 0 , val = ~ val ) ;
MATHICMD ( & , 0 , ) ;
MATHICMD ( | , 0 , ) ;
MATHICMD ( ^ ~ , 0 , ) ;
MATHICMD ( & ~ , 0 , ) ;
MATHICMD ( | ~ , 0 , ) ;
MATHCMD ( " << " , i , int , val = val2 < 32 ? val < < max ( val2 , 0 ) : 0 , 0 , ) ;
MATHCMD ( " >> " , i , int , val > > = clamp ( val2 , 0 , 31 ) , 0 , ) ;
MATHFCMD ( + , 0 , ) ;
MATHFCMD ( * , 1 , ) ;
MATHFCMD ( - , 0 , val = - val ) ;
CMPFCMDN ( = , = = ) ;
CMPFCMD ( ! = ) ;
CMPFCMD ( < ) ;
CMPFCMD ( > ) ;
CMPFCMD ( < = ) ;
CMPFCMD ( > = ) ;
ICOMMANDK ( ! , ID_NOT , " t " , ( tagval * a ) , intret ( getbool ( * a ) ? 0 : 1 ) ) ;
ICOMMANDK ( & & , ID_AND , " E1V " , ( tagval * args , int numargs ) ,
{
if ( ! numargs ) intret ( 1 ) ;
else loopi ( numargs )
{
if ( i ) freearg ( * commandret ) ;
if ( args [ i ] . type = = VAL_CODE ) executeret ( args [ i ] . code , * commandret ) ;
else * commandret = args [ i ] ;
if ( ! getbool ( * commandret ) ) break ;
}
} ) ;
ICOMMANDK ( | | , ID_OR , " E1V " , ( tagval * args , int numargs ) ,
{
if ( ! numargs ) intret ( 0 ) ;
else loopi ( numargs )
{
if ( i ) freearg ( * commandret ) ;
if ( args [ i ] . type = = VAL_CODE ) executeret ( args [ i ] . code , * commandret ) ;
else * commandret = args [ i ] ;
if ( getbool ( * commandret ) ) break ;
}
} ) ;
# define DIVCMD(name, fmt, type, op) MATHCMD(#name, fmt, type, { if(val2) op; else val = 0; }, 0, )
DIVCMD ( div , i , int , val / = val2 ) ;
DIVCMD ( mod , i , int , val % = val2 ) ;
DIVCMD ( divf , f , float , val / = val2 ) ;
DIVCMD ( modf , f , float , val = fmod ( val , val2 ) ) ;
MATHCMD ( " pow " , f , float , val = pow ( val , val2 ) , 0 , ) ;
ICOMMAND ( sin , " f " , ( float * a ) , floatret ( sin ( * a * RAD ) ) ) ;
ICOMMAND ( cos , " f " , ( float * a ) , floatret ( cos ( * a * RAD ) ) ) ;
ICOMMAND ( tan , " f " , ( float * a ) , floatret ( tan ( * a * RAD ) ) ) ;
ICOMMAND ( asin , " f " , ( float * a ) , floatret ( asin ( * a ) / RAD ) ) ;
ICOMMAND ( acos , " f " , ( float * a ) , floatret ( acos ( * a ) / RAD ) ) ;
ICOMMAND ( atan , " f " , ( float * a ) , floatret ( atan ( * a ) / RAD ) ) ;
ICOMMAND ( atan2 , " ff " , ( float * y , float * x ) , floatret ( atan2 ( * y , * x ) / RAD ) ) ;
ICOMMAND ( sqrt , " f " , ( float * a ) , floatret ( sqrt ( * a ) ) ) ;
ICOMMAND ( loge , " f " , ( float * a ) , floatret ( log ( * a ) ) ) ;
ICOMMAND ( log2 , " f " , ( float * a ) , floatret ( log ( * a ) / M_LN2 ) ) ;
ICOMMAND ( log10 , " f " , ( float * a ) , floatret ( log10 ( * a ) ) ) ;
ICOMMAND ( exp , " f " , ( float * a ) , floatret ( exp ( * a ) ) ) ;
# define MINMAXCMD(name, fmt, type, op) \
ICOMMAND ( name , # fmt " 1V " , ( tagval * args , int numargs ) , \
{ \
type val = numargs > 0 ? args [ 0 ] . fmt : 0 ; \
for ( int i = 1 ; i < numargs ; i + + ) val = op ( val , args [ i ] . fmt ) ; \
type # # ret ( val ) ; \
} )
MINMAXCMD ( min , i , int , min ) ;
MINMAXCMD ( max , i , int , max ) ;
MINMAXCMD ( minf , f , float , min ) ;
MINMAXCMD ( maxf , f , float , max ) ;
ICOMMAND ( bitscan , " i " , ( int * n ) , intret ( bitscan ( * n ) ) ) ;
ICOMMAND ( abs , " i " , ( int * n ) , intret ( abs ( * n ) ) ) ;
ICOMMAND ( absf , " f " , ( float * n ) , floatret ( fabs ( * n ) ) ) ;
ICOMMAND ( floor , " f " , ( float * n ) , floatret ( floor ( * n ) ) ) ;
ICOMMAND ( ceil , " f " , ( float * n ) , floatret ( ceil ( * n ) ) ) ;
ICOMMAND ( round , " ff " , ( float * n , float * k ) ,
{
double step = * k ;
double r = * n ;
if ( step > 0 )
{
r + = step * ( r < 0 ? - 0.5 : 0.5 ) ;
r - = fmod ( r , step ) ;
}
else r = r < 0 ? ceil ( r - 0.5 ) : floor ( r + 0.5 ) ;
floatret ( float ( r ) ) ;
} ) ;
ICOMMAND ( cond , " ee2V " , ( tagval * args , int numargs ) ,
{
for ( int i = 0 ; i < numargs ; i + = 2 )
{
if ( i + 1 < numargs )
{
if ( executebool ( args [ i ] . code ) )
{
executeret ( args [ i + 1 ] . code , * commandret ) ;
break ;
}
}
else
{
executeret ( args [ i ] . code , * commandret ) ;
break ;
}
}
} ) ;
# define CASECOMMAND(name, fmt, type, acc, compare) \
ICOMMAND ( name , fmt " te2V " , ( tagval * args , int numargs ) , \
{ \
type val = acc ; \
int i ; \
for ( i = 1 ; i + 1 < numargs ; i + = 2 ) \
{ \
if ( compare ) \
{ \
executeret ( args [ i + 1 ] . code , * commandret ) ; \
return ; \
} \
} \
} )
CASECOMMAND ( case , " i " , int , args [ 0 ] . getint ( ) , args [ i ] . type = = VAL_NULL | | args [ i ] . getint ( ) = = val ) ;
CASECOMMAND ( casef , " f " , float , args [ 0 ] . getfloat ( ) , args [ i ] . type = = VAL_NULL | | args [ i ] . getfloat ( ) = = val ) ;
CASECOMMAND ( cases , " s " , const char * , args [ 0 ] . getstr ( ) , args [ i ] . type = = VAL_NULL | | ! strcmp ( args [ i ] . getstr ( ) , val ) ) ;
ICOMMAND ( rnd , " ii " , ( int * a , int * b ) , intret ( * a - * b > 0 ? rnd ( * a - * b ) + * b : * b ) ) ;
ICOMMAND ( rndstr , " i " , ( int * len ) ,
{
int n = clamp ( * len , 0 , 10000 ) ;
char * s = newstring ( n ) ;
for ( int i = 0 ; i < n ; )
{
uint r = randomMT ( ) ;
for ( int j = min ( i + 4 , n ) ; i < j ; i + + )
{
s [ i ] = ( r % 255 ) + 1 ;
r / = 255 ;
}
}
s [ n ] = ' \0 ' ;
stringret ( s ) ;
} ) ;
ICOMMAND ( tohex , " ii " , ( int * n , int * p ) ,
{
const int len = 20 ;
char * buf = newstring ( len ) ;
nformatstring ( buf , len , " 0x%.*X " , max ( * p , 1 ) , * n ) ;
stringret ( buf ) ;
} ) ;
# define CMPSCMD(name, op) \
ICOMMAND ( name , " s1V " , ( tagval * args , int numargs ) , \
{ \
bool val ; \
if ( numargs > = 2 ) \
{ \
val = strcmp ( args [ 0 ] . s , args [ 1 ] . s ) op 0 ; \
for ( int i = 2 ; i < numargs & & val ; i + + ) val = strcmp ( args [ i - 1 ] . s , args [ i ] . s ) op 0 ; \
} \
else val = ( numargs > 0 ? args [ 0 ] . s [ 0 ] : 0 ) op 0 ; \
intret ( int ( val ) ) ; \
} )
CMPSCMD ( strcmp , = = ) ;
CMPSCMD ( = s , = = ) ;
CMPSCMD ( ! = s , ! = ) ;
CMPSCMD ( < s , < ) ;
CMPSCMD ( > s , > ) ;
CMPSCMD ( < = s , < = ) ;
CMPSCMD ( > = s , > = ) ;
ICOMMAND ( echo , " C " , ( char * s ) , conoutf ( " \f 1%s " , s ) ) ;
ICOMMAND ( error , " C " , ( char * s ) , conoutf ( CON_ERROR , " %s " , s ) ) ;
ICOMMAND ( strstr , " ss " , ( char * a , char * b ) , { char * s = strstr ( a , b ) ; intret ( s ? s - a : - 1 ) ; } ) ;
ICOMMAND ( strlen , " s " , ( char * s ) , intret ( strlen ( s ) ) ) ;
ICOMMAND ( strcode , " si " , ( char * s , int * i ) , intret ( * i > 0 ? ( memchr ( s , 0 , * i ) ? 0 : uchar ( s [ * i ] ) ) : uchar ( s [ 0 ] ) ) ) ;
ICOMMAND ( codestr , " i " , ( int * i ) , { char * s = newstring ( 1 ) ; s [ 0 ] = char ( * i ) ; s [ 1 ] = ' \0 ' ; stringret ( s ) ; } ) ;
ICOMMAND ( struni , " si " , ( char * s , int * i ) , intret ( * i > 0 ? ( memchr ( s , 0 , * i ) ? 0 : cube2uni ( s [ * i ] ) ) : cube2uni ( s [ 0 ] ) ) ) ;
ICOMMAND ( unistr , " i " , ( int * i ) , { char * s = newstring ( 1 ) ; s [ 0 ] = uni2cube ( * i ) ; s [ 1 ] = ' \0 ' ; stringret ( s ) ; } ) ;
# define STRMAPCOMMAND(name, map) \
ICOMMAND ( name , " s " , ( char * s ) , \
{ \
int len = strlen ( s ) ; \
char * m = newstring ( len ) ; \
loopi ( len ) m [ i ] = map ( s [ i ] ) ; \
m [ len ] = ' \0 ' ; \
stringret ( m ) ; \
} )
STRMAPCOMMAND ( strlower , cubelower ) ;
STRMAPCOMMAND ( strupper , cubeupper ) ;
char * strreplace ( const char * s , const char * oldval , const char * newval , const char * newval2 )
{
vector < char > buf ;
int oldlen = strlen ( oldval ) ;
if ( ! oldlen ) return newstring ( s ) ;
for ( int i = 0 ; ; i + + )
{
const char * found = strstr ( s , oldval ) ;
if ( found )
{
while ( s < found ) buf . add ( * s + + ) ;
for ( const char * n = i & 1 ? newval2 : newval ; * n ; n + + ) buf . add ( * n ) ;
s = found + oldlen ;
}
else
{
while ( * s ) buf . add ( * s + + ) ;
buf . add ( ' \0 ' ) ;
return newstring ( buf . getbuf ( ) , buf . length ( ) ) ;
}
}
}
ICOMMAND ( strreplace , " ssss " , ( char * s , char * o , char * n , char * n2 ) , commandret - > setstr ( strreplace ( s , o , n , n2 [ 0 ] ? n2 : n ) ) ) ;
void strsplice ( const char * s , const char * vals , int * skip , int * count )
{
int slen = strlen ( s ) , vlen = strlen ( vals ) ,
offset = clamp ( * skip , 0 , slen ) ,
len = clamp ( * count , 0 , slen - offset ) ;
char * p = newstring ( slen - len + vlen ) ;
if ( offset ) memcpy ( p , s , offset ) ;
if ( vlen ) memcpy ( & p [ offset ] , vals , vlen ) ;
if ( offset + len < slen ) memcpy ( & p [ offset + vlen ] , & s [ offset + len ] , slen - ( offset + len ) ) ;
p [ slen - len + vlen ] = ' \0 ' ;
commandret - > setstr ( p ) ;
}
COMMAND ( strsplice , " ssii " ) ;
# ifndef STANDALONE
ICOMMAND ( getmillis , " i " , ( int * total ) , intret ( * total ? totalmillis : lastmillis ) ) ;
struct sleepcmd
{
int delay , millis , flags ;
char * command ;
} ;
vector < sleepcmd > sleepcmds ;
void addsleep ( int * msec , char * cmd )
{
sleepcmd & s = sleepcmds . add ( ) ;
s . delay = max ( * msec , 1 ) ;
s . millis = lastmillis ;
s . command = newstring ( cmd ) ;
s . flags = identflags ;
}
COMMANDN ( sleep , addsleep , " is " ) ;
void checksleep ( int millis )
{
loopv ( sleepcmds )
{
sleepcmd & s = sleepcmds [ i ] ;
if ( millis - s . millis > = s . delay )
{
char * cmd = s . command ; // execute might create more sleep commands
2020-07-30 03:15:44 +02:00
s . command = nullptr ;
2020-04-15 18:39:17 +02:00
int oldflags = identflags ;
identflags = s . flags ;
execute ( cmd ) ;
identflags = oldflags ;
delete [ ] cmd ;
if ( sleepcmds . inrange ( i ) & & ! sleepcmds [ i ] . command ) sleepcmds . remove ( i - - ) ;
}
}
}
void clearsleep ( bool clearoverrides )
{
int len = 0 ;
loopv ( sleepcmds ) if ( sleepcmds [ i ] . command )
{
if ( clearoverrides & & ! ( sleepcmds [ i ] . flags & IDF_OVERRIDDEN ) ) sleepcmds [ len + + ] = sleepcmds [ i ] ;
else delete [ ] sleepcmds [ i ] . command ;
}
sleepcmds . shrink ( len ) ;
}
void clearsleep_ ( int * clearoverrides )
{
clearsleep ( * clearoverrides ! = 0 | | identflags & IDF_OVERRIDDEN ) ;
}
COMMANDN ( clearsleep , clearsleep_ , " i " ) ;
# endif