summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaru <haru@dotalux.com>2013-09-09 12:47:48 +0200
committerHaru <haru@dotalux.com>2013-11-28 01:30:13 +0100
commitac6ae8c932efbca30ef1650fa5d7bd94ead336f5 (patch)
treeb69f10556ad98f894c7c5346f57670debfd8961e
parent83d2d9343ab965c22816ac231973205ee67035e1 (diff)
downloadhercules-ac6ae8c932efbca30ef1650fa5d7bd94ead336f5.tar.gz
hercules-ac6ae8c932efbca30ef1650fa5d7bd94ead336f5.tar.bz2
hercules-ac6ae8c932efbca30ef1650fa5d7bd94ead336f5.tar.xz
hercules-ac6ae8c932efbca30ef1650fa5d7bd94ead336f5.zip
Added pre de/increment operators. Fixed post de/increment operators.
- [ This commit is part of a larger script engine related update ] - Suffix ++ and -- operators now behave like in other languages (updating the variable *after* its value is returned.) - Prefix ++ and -- operators are added for parity with other scripting/programming languages. They update the variable they're applied to *before* returning its value. - Please note that the implementation of the prefix form of those operators (like it happens in most languages) is more efficient than the suffix form. '++.@i' is (slightly) faster than '.@i++', or at least not slower. - Fixed some outdated script debug functions. - Follow-up to c18f438. Signed-off-by: Haru <haru@dotalux.com>
-rw-r--r--src/map/script.c246
-rw-r--r--src/map/script.h6
2 files changed, 150 insertions, 102 deletions
diff --git a/src/map/script.c b/src/map/script.c
index fd16bdbad..2c4178d9b 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -66,11 +66,9 @@ static inline void SETVALUE(unsigned char* buf, int i, int n) {
struct script_interface script_s;
-const char* script_op2name(int op)
-{
+const char* script_op2name(int op) {
#define RETURN_OP_NAME(type) case type: return #type
- switch( op )
- {
+ switch( op ) {
RETURN_OP_NAME(C_NOP);
RETURN_OP_NAME(C_POS);
RETURN_OP_NAME(C_INT);
@@ -85,6 +83,8 @@ const char* script_op2name(int op)
RETURN_OP_NAME(C_USERFUNC);
RETURN_OP_NAME(C_USERFUNC_POS);
+ RETURN_OP_NAME(C_REF);
+
// operators
RETURN_OP_NAME(C_OP3);
RETURN_OP_NAME(C_LOR);
@@ -108,6 +108,10 @@ const char* script_op2name(int op)
RETURN_OP_NAME(C_NOT);
RETURN_OP_NAME(C_R_SHIFT);
RETURN_OP_NAME(C_L_SHIFT);
+ RETURN_OP_NAME(C_ADD_POST);
+ RETURN_OP_NAME(C_SUB_POST);
+ RETURN_OP_NAME(C_ADD_PRE);
+ RETURN_OP_NAME(C_SUB_PRE);
default:
ShowDebug("script_op2name: unexpected op=%d\n", op);
@@ -819,6 +823,38 @@ void parse_nextline(bool first, const char* p)
script->str_data[LABEL_NEXTLINE].label = -1;
}
+/**
+ * Pushes a variable into stack, processing its array index if needed.
+ * @see parse_variable
+ */
+void parse_variable_sub_push(int word, const char *p2) {
+ const char* p3 = NULL;
+
+ if( p2 ) {
+ // process the variable index
+
+ // push the getelementofarray method into the stack
+ script->addl(script->buildin_getelementofarray_ref);
+ script->addc(C_ARG);
+ script->addl(word);
+
+ // process the sub-expression for this assignment
+ p3 = script->parse_subexpr(p2 + 1, 1);
+ p3 = script->skip_space(p3);
+
+ if( *p3 != ']' ) {// closing parenthesis is required for this script
+ disp_error_message("Missing closing ']' parenthesis for the variable assignment.", p3);
+ }
+
+ // push the closing function stack operator onto the stack
+ script->addc(C_FUNC);
+ p3++;
+ } else {
+ // No array index, simply push the variable or value onto the stack
+ script->addl(word);
+ }
+}
+
/// Parse a variable assignment using the direct equals operator
/// @param p script position where the function should run from
/// @return NULL if not a variable assignment, the new position otherwise
@@ -828,21 +864,30 @@ const char* parse_variable(const char* p) {
const char *p2 = NULL;
const char *var = p;
+ if( ( p[0] == '+' && p[1] == '+' && (type = C_ADD_PRE) ) // pre ++
+ || ( p[1] == '-' && p[1] == '-' && (type = C_SUB_PRE) ) // pre --
+ ) {
+ var = p = script->skip_space(&p[2]);
+ }
+
// skip the variable where applicable
p = script->skip_word(p);
p = script->skip_space(p);
- if( p == NULL ) {// end of the line or invalid buffer
+ if( p == NULL ) {
+ // end of the line or invalid buffer
return NULL;
}
- if( *p == '[' ) {// array variable so process the array as appropriate
+ if( *p == '[' ) {
+ // array variable so process the array as appropriate
for( p2 = p, i = 0, j = 1; p; ++ i ) {
if( *p ++ == ']' && --(j) == 0 ) break;
if( *p == '[' ) ++ j;
}
- if( !(p = script->skip_space(p)) ) {// end of line or invalid characters remaining
+ if( !(p = script->skip_space(p)) ) {
+ // end of line or invalid characters remaining
disp_error_message("Missing right expression or closing bracket for variable.", p);
}
}
@@ -857,8 +902,8 @@ const char* parse_variable(const char* p) {
|| ( p[0] == '*' && p[1] == '=' && (type = C_MUL) ) // *=
|| ( p[0] == '/' && p[1] == '=' && (type = C_DIV) ) // /=
|| ( p[0] == '%' && p[1] == '=' && (type = C_MOD) ) // %=
- || ( p[0] == '+' && p[1] == '+' && (type = C_ADD_PP) ) // ++
- || ( p[0] == '-' && p[1] == '-' && (type = C_SUB_PP) ) // --
+ || ( p[0] == '+' && p[1] == '+' && (type = C_ADD_POST) ) // post ++
+ || ( p[0] == '-' && p[1] == '-' && (type = C_SUB_POST) ) // post --
|| ( p[0] == '<' && p[1] == '<' && p[2] == '=' && (type = C_L_SHIFT) ) // <<=
|| ( p[0] == '>' && p[1] == '>' && p[2] == '=' && (type = C_R_SHIFT) ) // >>=
) )
@@ -867,23 +912,26 @@ const char* parse_variable(const char* p) {
}
switch( type ) {
- case C_EQ: {// incremental modifier
+ case C_ADD_PRE: // pre ++
+ case C_SUB_PRE: // pre --
+ // (nothing more to skip)
+ break;
+
+ case C_EQ: // =
p = script->skip_space( &p[1] );
- }
- break;
+ break;
- case C_L_SHIFT:
- case C_R_SHIFT: {// left or right shift modifier
+ case C_L_SHIFT: // <<=
+ case C_R_SHIFT: // >>=
p = script->skip_space( &p[3] );
- }
- break;
+ break;
- default: {// normal incremental command
+ default: // everything else
p = script->skip_space( &p[2] );
- }
}
- if( p == NULL ) {// end of line or invalid buffer
+ if( p == NULL ) {
+ // end of line or invalid buffer
return NULL;
}
@@ -897,56 +945,44 @@ const char* parse_variable(const char* p) {
script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_PAREN;
// increment the total curly count for the position in the script
- ++ script->syntax.curly_count;
+ ++script->syntax.curly_count;
// parse the variable currently being modified
word = script->add_word(var);
- if( script->str_data[word].type == C_FUNC || script->str_data[word].type == C_USERFUNC || script->str_data[word].type == C_USERFUNC_POS )
- {// cannot assign a variable which exists as a function or label
+ if( script->str_data[word].type == C_FUNC
+ || script->str_data[word].type == C_USERFUNC
+ || script->str_data[word].type == C_USERFUNC_POS
+ ) {
+ // cannot assign a variable which exists as a function or label
disp_error_message("Cannot modify a variable which has the same name as a function or label.", p);
}
- if( p2 ) {// process the variable index
- const char* p3 = NULL;
-
- // push the getelementofarray method into the stack
- script->addl(script->buildin_getelementofarray_ref);
- script->addc(C_ARG);
- script->addl(word);
-
- // process the sub-expression for this assignment
- p3 = script->parse_subexpr(p2 + 1, 1);
- p3 = script->skip_space(p3);
-
- if( *p3 != ']' ) {// closing parenthesis is required for this script
- disp_error_message("Missing closing ']' parenthesis for the variable assignment.", p3);
- }
-
- // push the closing function stack operator onto the stack
- script->addc(C_FUNC);
- p3 ++;
- } else {// simply push the variable or value onto the stack
- script->addl(word);
- }
+ parse_variable_sub_push(word, p2); // Push variable onto the stack
if( type != C_EQ )
script->addc(C_REF);
- if( type == C_ADD_PP || type == C_SUB_PP ) {// incremental operator for the method
+ if( type == C_ADD_POST || type == C_SUB_POST ) { // post ++ / --
+ script->addi(1);
+ script->addc(type == C_ADD_POST ? C_ADD : C_SUB);
+
+ parse_variable_sub_push(word, p2); // Push variable onto the stack (third argument of setr)
+ } else if( type == C_ADD_PRE || type == C_SUB_PRE ) { // pre ++ / --
script->addi(1);
- script->addc(type == C_ADD_PP ? C_ADD : C_SUB);
- } else {// process the value as an expression
+ script->addc(type == C_ADD_PRE ? C_ADD : C_SUB);
+ } else {
+ // process the value as an expression
p = script->parse_subexpr(p, -1);
- if( type != C_EQ )
- {// push the type of modifier onto the stack
+ if( type != C_EQ ) {
+ // push the type of modifier onto the stack
script->addc(type);
}
}
// decrement the curly count for the position within the script
- -- script->syntax.curly_count;
+ --script->syntax.curly_count;
// close the script by appending the function operator
script->addc(C_FUNC);
@@ -1104,48 +1140,51 @@ const char* parse_simpleexpr(const char *p) {
/*==========================================
* Analysis of the expression
*------------------------------------------*/
-const char* script_parse_subexpr(const char* p,int limit)
-{
+const char* script_parse_subexpr(const char* p,int limit) {
int op,opl,len;
const char* tmpp;
p=script->skip_space(p);
- if( *p == '-' ){
+ if( *p == '-' ) {
tmpp = script->skip_space(p+1);
- if( *tmpp == ';' || *tmpp == ',' ){
+ if( *tmpp == ';' || *tmpp == ',' ) {
script->addl(LABEL_NEXTLINE);
p++;
return p;
}
}
- if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){
+ if( (op=C_ADD_PRE,p[0]=='+'&&p[1]=='+') || (op=C_SUB_PRE,p[0]=='-'&&p[1]=='-') ) { // Pre ++ -- operators
+ p=script->parse_variable(p);
+ } else if( (op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~') ) { // Unary - ! ~ operators
p=script->parse_subexpr(p+1,10);
script->addc(op);
- } else
+ } else {
p=script->parse_simpleexpr(p);
+ }
p=script->skip_space(p);
while((
- (op=C_OP3,opl=0,len=1,*p=='?') ||
- (op=C_ADD,opl=8,len=1,*p=='+') ||
- (op=C_SUB,opl=8,len=1,*p=='-') ||
- (op=C_MUL,opl=9,len=1,*p=='*') ||
- (op=C_DIV,opl=9,len=1,*p=='/') ||
- (op=C_MOD,opl=9,len=1,*p=='%') ||
- (op=C_LAND,opl=2,len=2,*p=='&' && p[1]=='&') ||
- (op=C_AND,opl=6,len=1,*p=='&') ||
- (op=C_LOR,opl=1,len=2,*p=='|' && p[1]=='|') ||
- (op=C_OR,opl=5,len=1,*p=='|') ||
- (op=C_XOR,opl=4,len=1,*p=='^') ||
- (op=C_EQ,opl=3,len=2,*p=='=' && p[1]=='=') ||
- (op=C_NE,opl=3,len=2,*p=='!' && p[1]=='=') ||
- (op=C_R_SHIFT,opl=7,len=2,*p=='>' && p[1]=='>') ||
- (op=C_GE,opl=3,len=2,*p=='>' && p[1]=='=') ||
- (op=C_GT,opl=3,len=1,*p=='>') ||
- (op=C_L_SHIFT,opl=7,len=2,*p=='<' && p[1]=='<') ||
- (op=C_LE,opl=3,len=2,*p=='<' && p[1]=='=') ||
- (op=C_LT,opl=3,len=1,*p=='<')) && opl>limit){
+ (op=C_OP3, opl=0,len=1,*p=='?') // ?:
+ || (op=C_ADD, opl=8,len=1,*p=='+') // +
+ || (op=C_SUB, opl=8,len=1,*p=='-') // -
+ || (op=C_MUL, opl=9,len=1,*p=='*') // *
+ || (op=C_DIV, opl=9,len=1,*p=='/') // /
+ || (op=C_MOD, opl=9,len=1,*p=='%') // %
+ || (op=C_LAND, opl=2,len=2,*p=='&' && p[1]=='&') // &&
+ || (op=C_AND, opl=6,len=1,*p=='&') // &
+ || (op=C_LOR, opl=1,len=2,*p=='|' && p[1]=='|') // ||
+ || (op=C_OR, opl=5,len=1,*p=='|') // |
+ || (op=C_XOR, opl=4,len=1,*p=='^') // ^
+ || (op=C_EQ, opl=3,len=2,*p=='=' && p[1]=='=') // ==
+ || (op=C_NE, opl=3,len=2,*p=='!' && p[1]=='=') // !=
+ || (op=C_R_SHIFT,opl=7,len=2,*p=='>' && p[1]=='>') // >>
+ || (op=C_GE, opl=3,len=2,*p=='>' && p[1]=='=') // >=
+ || (op=C_GT, opl=3,len=1,*p=='>') // >
+ || (op=C_L_SHIFT,opl=7,len=2,*p=='<' && p[1]=='<') // <<
+ || (op=C_LE, opl=3,len=2,*p=='<' && p[1]=='=') // <=
+ || (op=C_LT, opl=3,len=1,*p=='<') // <
+ ) && opl>limit) {
p+=len;
if(op == C_OP3) {
p=script->parse_subexpr(p,-1);
@@ -2329,7 +2368,7 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o
i += 3;
break;
case C_STR:
- j = strlen(script->buf + i);
+ j = strlen((char*)script->buf + i);
ShowMessage(" %s", script->buf + i);
i += j+1;
break;
@@ -5120,8 +5159,7 @@ BUILDIN(copyarray);
/// The value is converted to the type of the variable.
///
/// set(<variable>,<value>) -> <variable>
-BUILDIN(set)
-{
+BUILDIN(setr) {
TBL_PC* sd = NULL;
struct script_data* data;
//struct script_data* datavalue;
@@ -5131,8 +5169,7 @@ BUILDIN(set)
data = script_getdata(st,2);
//datavalue = script_getdata(st,3);
- if( !data_isreference(data) )
- {
+ if( !data_isreference(data) ) {
ShowError("script:set: not a variable\n");
script->reportdata(script_getdata(st,2));
st->state = END;
@@ -5143,31 +5180,30 @@ BUILDIN(set)
name = reference_getname(data);
prefix = *name;
- if( not_server_variable(prefix) )
- {
+ if( not_server_variable(prefix) ) {
sd = script->rid2sd(st);
- if( sd == NULL )
- {
+ if( sd == NULL ) {
ShowError("script:set: no player attached for player variable '%s'\n", name);
return true;
}
}
#if 0
- if( data_isreference(datavalue) )
- {// the value being referenced is a variable
+ // TODO: see de43fa0f73be01080bd11c08adbfb7c158324c81
+ if( data_isreference(datavalue) ) {
+ // the value being referenced is a variable
const char* namevalue = reference_getname(datavalue);
- if( !not_array_variable(*namevalue) )
- {// array variable being copied into another array variable
- if( sd == NULL && not_server_variable(*namevalue) && !(sd = script->rid2sd(st)) )
- {// player must be attached in order to copy a player variable
+ if( !not_array_variable(*namevalue) ) {
+ // array variable being copied into another array variable
+ if( sd == NULL && not_server_variable(*namevalue) && !(sd = script->rid2sd(st)) ) {
+ // player must be attached in order to copy a player variable
ShowError("script:set: no player attached for player variable '%s'\n", namevalue);
return true;
}
- if( is_string_variable(namevalue) != is_string_variable(name) )
- {// non-matching array value types
+ if( is_string_variable(namevalue) != is_string_variable(name) ) {
+ // non-matching array value types
ShowWarning("script:set: two array variables do not match in type.\n");
return true;
}
@@ -5181,14 +5217,23 @@ BUILDIN(set)
}
#endif
+ if( script_hasdata(st, 4) ) {
+ // Optional argument used by post-increment/post-decrement constructs to return the previous value
+ if( is_string_variable(name) ) {
+ script_pushstrcopy(st, script_getstr(st, 4));
+ } else {
+ script_pushint(st, script_getnum(st, 4));
+ }
+ } else {
+ // return a copy of the variable reference
+ script_pushcopy(st,2);
+ }
+
if( is_string_variable(name) )
script->set_reg(st,sd,num,name,(void*)script_getstr(st,3),script_getref(st,2));
else
script->set_reg(st,sd,num,name,(void*)__64BPTRSIZE(script_getnum(st,3)),script_getref(st,2));
- // return a copy of the variable reference
- script_pushcopy(st,2);
-
return true;
}
@@ -17993,7 +18038,8 @@ void script_parse_builtin(void) {
BUILDIN_DEF(warpguild,"siii"), // [Fredzilla]
BUILDIN_DEF(setlook,"ii"),
BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it
- BUILDIN_DEF(set,"rv"),
+ BUILDIN_DEF2(setr,"set","rv"),
+ BUILDIN_DEF(setr,"rv?"), // Not meant to be used directly, required for var++/var--
BUILDIN_DEF(setarray,"rv*"),
BUILDIN_DEF(cleararray,"rvi"),
BUILDIN_DEF(copyarray,"rri"),
@@ -18486,10 +18532,10 @@ void script_parse_builtin(void) {
int slen = strlen(BUILDIN[i].arg), offset = start + i;
n = script->add_str(BUILDIN[i].name);
- if (!strcmp(BUILDIN[i].name, "set")) script->buildin_set_ref = n;
- else if (!strcmp(BUILDIN[i].name, "callsub")) script->buildin_callsub_ref = n;
- else if (!strcmp(BUILDIN[i].name, "callfunc")) script->buildin_callfunc_ref = n;
- else if (!strcmp(BUILDIN[i].name, "getelementofarray") ) script->buildin_getelementofarray_ref = n;
+ if (!strcmp(BUILDIN[i].name, "setr")) script->buildin_set_ref = n;
+ else if (!strcmp(BUILDIN[i].name, "callsub")) script->buildin_callsub_ref = n;
+ else if (!strcmp(BUILDIN[i].name, "callfunc")) script->buildin_callfunc_ref = n;
+ else if (!strcmp(BUILDIN[i].name, "getelementofarray") ) script->buildin_getelementofarray_ref = n;
if( script->str_data[n].func && script->str_data[n].func != BUILDIN[i].func )
continue;/* something replaced it, skip. */
diff --git a/src/map/script.h b/src/map/script.h
index e0e5f9ea9..75a57d82b 100644
--- a/src/map/script.h
+++ b/src/map/script.h
@@ -196,8 +196,10 @@ typedef enum c_op {
C_NOT, // ~ a
C_R_SHIFT, // a >> b
C_L_SHIFT, // a << b
- C_ADD_PP, // ++a
- C_SUB_PP, // --a
+ C_ADD_POST, // a++
+ C_SUB_POST, // a--
+ C_ADD_PRE, // ++a
+ C_SUB_PRE, // --a
} c_op;
enum hQueueOpt {