void VDScriptInterpreter::ExecuteLine(const char *s) { int t; mErrorExtraToken.clear(); TokenBegin(s); while(t = Token()) { if (isExprFirstToken(t)) { TokenUnput(t); VDASSERT(mStack.empty()); ParseExpression(); VDASSERT(!mStack.empty()); VDScriptValue& val = mStack.back(); mStack.pop_back(); VDASSERT(mStack.empty()); if (Token() != ';') SCRIPT_ERROR(SEMICOLON_EXPECTED); } else if (t == TOK_DECLARE) { do { t = Token(); if (t != TOK_IDENT) SCRIPT_ERROR(IDENTIFIER_EXPECTED); VariableTableEntry *vte = vartbl.Declare(szIdent); t = Token(); if (t == '=') { ParseExpression(); VDASSERT(!mStack.empty()); vte->v = mStack.back(); mStack.pop_back(); t = Token(); } } while(t == ','); if (t != ';') SCRIPT_ERROR(SEMICOLON_EXPECTED); } else SCRIPT_ERROR(PARSE_ERROR); } VDASSERT(mStack.empty()); GC(); }
VDScriptValue VDScriptInterpreter::LookupRootVariable(char *szName) { VariableTableEntry *vte; if (vte = vartbl.Lookup(szName)) return VDScriptValue(vte); if (!strcmp(szName, "Sylia")) return VDScriptValue(NULL, &obj_Sylia); const char *volatile _szName = szName; // needed to fix exception handler, for some reason VDScriptValue ret; try { if (!lpRoothandler) SCRIPT_ERROR(VAR_NOT_FOUND); ret = lpRoothandler(this, szName, lpRoothandlerData); } catch(const VDScriptError& e) { if (e.err == VDScriptError::VAR_NOT_FOUND) { mErrorExtraToken = _szName; throw; } } return ret; }
void VDScriptInterpreter::InvokeMethod(const VDScriptObject *obj, const char *name, int argc) { if (obj->func_list) { const VDScriptFunctionDef *sfd = obj->func_list; while(sfd->arg_list) { if (sfd->name && !strcmp(sfd->name, name)) { InvokeMethod(sfd, argc); return; } ++sfd; } } SCRIPT_ERROR(OVERLOADED_FUNCTION_NOT_FOUND); }
VariableTableEntry *VariableTable::Declare(const char *szName) { VariableTableEntry *vte; long lHashVal = Hash(szName); long lNameLen; lNameLen = strlen(szName); vte = (VariableTableEntry *)varheap.Allocate(sizeof(VariableTableEntry) + lNameLen); if (!vte) SCRIPT_ERROR(OUT_OF_MEMORY); vte->next = lpHashTable[lHashVal]; vte->v = VDScriptValue(); strcpy(vte->szName, szName); lpHashTable[lHashVal] = vte; return vte; }
void VDScriptInterpreter::Reduce() { const int op = mOpStack.back(); mOpStack.pop_back(); switch(op) { case '=': { VDScriptValue& v = mStack[mStack.size() - 2]; if (!v.isVarLV()) SCRIPT_ERROR(TYPE_OBJECT_REQUIRED); ConvertToRvalue(); v.asVarLV()->v = mStack.back(); mStack.pop_back(); } break; case TOK_OR: InvokeMethod(&obj_Sylia, "||", 2); break; case TOK_AND: InvokeMethod(&obj_Sylia, "&&", 2); break; case '|': InvokeMethod(&obj_Sylia, "|", 2); break; case '^': InvokeMethod(&obj_Sylia, "^", 2); break; case '&': InvokeMethod(&obj_Sylia, "&", 2); break; case TOK_EQUALS: InvokeMethod(&obj_Sylia, "==", 2); break; case TOK_NOTEQ: InvokeMethod(&obj_Sylia, "!=", 2); break; case '<': InvokeMethod(&obj_Sylia, "<", 2); break; case '>': InvokeMethod(&obj_Sylia, ">", 2); break; case TOK_LESSEQ: InvokeMethod(&obj_Sylia, "<=", 2); break; case TOK_GRTREQ: InvokeMethod(&obj_Sylia, ">=", 2); break; case '+': InvokeMethod(&obj_Sylia, "+", 2); break; case '-': InvokeMethod(&obj_Sylia, "-", 2); break; case '*': InvokeMethod(&obj_Sylia, "*", 2); break; case '/': InvokeMethod(&obj_Sylia, "/", 2); break; case '%': InvokeMethod(&obj_Sylia, "%", 2); break; } }
/* * parse a function and its arguments and fill the structure */ int encode_function(char *string, struct filter_op *fop) { char *str = strdup(string); int ret = -ENOTFOUND; char *name, *args; int nargs = 0, i; char **dec_args = NULL; char *tok; memset(fop, 0, sizeof(struct filter_op)); /* get the name of the function */ name = ec_strtok(string, "(", &tok); /* get all the args */ args = name + strlen(name) + 1; /* analyze the arguments */ dec_args = decode_args(args, &nargs); /* this fop is a function */ fop->opcode = FOP_FUNC; /* check if it is a known function */ if (!strcmp(name, "search")) { if (nargs == 2) { /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_SEARCH; fop->op.func.string = (u_char*)strdup(dec_args[1]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "regex")) { if (nargs == 2) { int err; regex_t regex; char errbuf[100]; /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_REGEX; fop->op.func.string = (u_char*)strdup(dec_args[1]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); /* check if the regex is valid */ err = regcomp(®ex, (const char*)fop->op.func.string, REG_EXTENDED | REG_NOSUB | REG_ICASE ); if (err) { regerror(err, ®ex, errbuf, sizeof(errbuf)); SCRIPT_ERROR("%s", errbuf); } regfree(®ex); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "pcre_regex")) { #ifndef HAVE_PCRE WARNING("The script contains pcre_regex, but you don't have support for it."); #else pcre *pregex; const char *errbuf = NULL; int erroff; if (nargs == 2) { /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_PCRE; fop->op.func.string = strdup(dec_args[1]); fop->op.func.slen = strlen(fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); /* check if the pcre is valid */ pregex = pcre_compile(fop->op.func.string, 0, &errbuf, &erroff, NULL ); if (pregex == NULL) SCRIPT_ERROR("%s\n", errbuf); pcre_free(pregex); } else if (nargs == 3) { fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_PCRE; /* substitution always at layer DATA */ fop->op.func.level = 5; fop->op.func.string = strdup(dec_args[1]); fop->op.func.slen = strlen(fop->op.func.string); fop->op.func.replace = strdup(dec_args[2]); fop->op.func.rlen = strlen(fop->op.func.replace); ret = ESUCCESS; /* check if the pcre is valid */ pregex = pcre_compile(fop->op.func.string, 0, &errbuf, &erroff, NULL ); if (pregex == NULL) SCRIPT_ERROR("%s\n", errbuf); pcre_free(pregex); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); #endif } else if (!strcmp(name, "replace")) { if (nargs == 2) { fop->op.func.op = FFUNC_REPLACE; /* replace always operate at DATA level */ fop->op.func.level = 5; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); fop->op.func.replace = (u_char*)strdup(dec_args[1]); fop->op.func.rlen = strescape((char*)fop->op.func.replace, (char*)fop->op.func.replace); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "inject")) { if (nargs == 1) { fop->op.func.op = FFUNC_INJECT; /* inject always operate at DATA level */ fop->op.func.level = 5; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "execinject")) { if (nargs == 1) { fop->op.func.op = FFUNC_EXECINJECT; /* execinject always operate at DATA level */ fop->op.func.level = 5; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "log")) { if (nargs == 2) { /* get the level (DATA or DECODED) */ if (encode_offset(dec_args[0], fop) == ESUCCESS) { /* encode offset wipe the fop !! */ fop->opcode = FOP_FUNC; fop->op.func.op = FFUNC_LOG; fop->op.func.string = (u_char*)strdup(dec_args[1]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Unknown offset %s ", dec_args[0]); } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "drop")) { if (nargs == 0) { fop->op.func.op = FFUNC_DROP; ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "kill")) { if (nargs == 0) { fop->op.func.op = FFUNC_KILL; ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "msg")) { if (nargs == 1) { fop->op.func.op = FFUNC_MSG; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "exec")) { if (nargs == 1) { fop->op.func.op = FFUNC_EXEC; fop->op.func.string = (u_char*)strdup(dec_args[0]); fop->op.func.slen = strlen((const char*)fop->op.func.string); ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } else if (!strcmp(name, "exit")) { if (nargs == 0) { fop->opcode = FOP_EXIT; ret = ESUCCESS; } else SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name); } /* free the array */ for (i = 0; i < nargs; i++) SAFE_FREE(dec_args[i]); SAFE_FREE(dec_args); SAFE_FREE(str); return ret; }
int VDScriptInterpreter::Token() { static char hexdig[]="0123456789ABCDEF"; char *s,c; if (tokhold) { int t = tokhold; tokhold = 0; return t; } do { c=*tokstr++; } while(c && isspace((unsigned char)c)); if (!c) { --tokstr; return 0; } // C++ style comment? if (c=='/') if (tokstr[0]=='/') { while(*tokstr) ++tokstr; return 0; // C++ comment } else return '/'; // string? if (c=='"') { const char *s = tokstr; char *t; long len_adjust=0; while((c=*tokstr++) && c!='"') { if (c=='\\') { c = *tokstr++; if (!c) SCRIPT_ERROR(PARSE_ERROR); else { if (c=='x') { if (!isxdigit((unsigned char)tokstr[0]) || !isxdigit((unsigned char)tokstr[1])) SCRIPT_ERROR(PARSE_ERROR); tokstr+=2; len_adjust += 2; } ++len_adjust; } } } tokslit = strheap.Allocate(tokstr - s - 1 - len_adjust); t = *tokslit; while(s<tokstr-1) { int val; c = *s++; if (c=='\\') switch(c=*s++) { case 'a': *t++='\a'; break; case 'b': *t++='\b'; break; case 'f': *t++='\f'; break; case 'n': *t++='\n'; break; case 'r': *t++='\r'; break; case 't': *t++='\t'; break; case 'v': *t++='\v'; break; case 'x': val = strchr(hexdig,toupper(s[0]))-hexdig; val = (val<<4) | (strchr(hexdig,toupper(s[1]))-hexdig); *t++ = val; s += 2; break; default: *t++ = c; } else *t++ = c; } *t=0; if (!c) --tokstr; return TOK_STRING; } // unescaped string? if ((c=='u' || c=='U') && *tokstr == '"') { const char *s = ++tokstr; while((c=*tokstr++) && c != '"') ; if (!c) { --tokstr; SCRIPT_ERROR(PARSE_ERROR); } size_t len = tokstr - s - 1; const VDStringA strA(VDTextWToU8(VDTextAToW(s, len))); len = strA.size(); tokslit = strheap.Allocate(len); memcpy(*tokslit, strA.data(), len); (*tokslit)[len] = 0; return TOK_STRING; } // look for variable/keyword if (isIdentFirstChar(c)) { s = szIdent; *s++ = c; while(isIdentNextChar(c = *tokstr++)) { if (s>=szIdent + MAX_IDENT_CHARS) SCRIPT_ERROR(IDENT_TOO_LONG); *s++ = c; } --tokstr; *s=0; if (!strcmp(szIdent, "declare")) return TOK_DECLARE; else if (!strcmp(szIdent, "true")) return TOK_TRUE; else if (!strcmp(szIdent, "false")) return TOK_FALSE; else if (!strcmp(szIdent, "int")) return TOK_INT; else if (!strcmp(szIdent, "long")) return TOK_LONG; else if (!strcmp(szIdent, "double")) return TOK_DOUBLE; return TOK_IDENT; } // test for number: decimal (123), octal (0123), or hexadecimal (0x123) if (isdigit((unsigned char)c)) { sint64 v = 0; if (c=='0' && tokstr[0] == 'x') { // hex (base 16) ++tokstr; while(isxdigit((unsigned char)(c = *tokstr++))) { v = v*16 + (strchr(hexdig, toupper(c))-hexdig); } } else if (c=='0' && isdigit((unsigned char)tokstr[0])) { // octal (base 8) while((c=*tokstr++)>='0' && c<='7') v = v*8 + (c-'0'); } else { // check for float const char *s = tokstr; while(*s) { if (*s == '.' || *s == 'e' || *s == 'E') { // It's a float -- parse and return. --tokstr; tokdval = strtod(tokstr, (char **)&tokstr); return TOK_DBLVAL; } if (!isdigit((unsigned char)*s)) break; ++s; } // decimal v = (c-'0'); while(isdigit((unsigned char)(c=*tokstr++))) v = v*10 + (c-'0'); } --tokstr; if (v > 0x7FFFFFFF) { toklval = v; return TOK_LONGVAL; } else { tokival = (int)v; return TOK_INTVAL; } } // test for symbols: // // charset: +-*/<>=!&|^[]~;%(), // solitary: +-*/<>=!&|^[]~;%(), // extra: != <= >= == && || // // the '/' is handled above for comment handling if (c=='!') if (tokstr[0] == '=') { ++tokstr; return TOK_NOTEQ; } else return '!'; if (c=='<') if (tokstr[0] == '=') { ++tokstr; return TOK_LESSEQ; } else return '<'; if (c=='>') if (tokstr[0] == '=') { ++tokstr; return TOK_GRTREQ; } else return '>'; if (c=='=') if (tokstr[0] == '=') { ++tokstr; return TOK_EQUALS; } else return '='; if (c=='&') if (tokstr[0] == '&') { ++tokstr; return TOK_AND; } else return '&'; if (c=='|') if (tokstr[0] == '|') { ++tokstr; return TOK_OR; } else return '|'; if (strchr("+-*^[]~;%(),.",c)) return c; SCRIPT_ERROR(PARSE_ERROR); }
void VDScriptInterpreter::InvokeMethod(const VDScriptFunctionDef *sfd, int pcount) { VDScriptValue *params = NULL; if (pcount) params = &mStack[mStack.size() - pcount]; // convert params to rvalues for(int j=0; j<pcount; ++j) { VDScriptValue& parm = params[j]; if (parm.isVarLV()) parm = parm.asVarLV()->v; } mpCurrentInvocationMethod = sfd; mMethodArgumentCount = pcount; // If we were passed a function name, attempt to match our parameter // list to one of the overloaded function templates. // // 0 = void, v = value, i = int, . = varargs // // <return value><param1><param2>... const char *name = sfd->name; // Yes, goto's are usually considered gross... but I prefer // cleanly labeled goto's to excessive boolean variable usage. const VDScriptFunctionDef *sfd_best = NULL; int *best_scores = (int *)_alloca(sizeof(int) * (pcount + 1)); int *current_scores = (int *)_alloca(sizeof(int) * (pcount + 1)); int best_promotions = 0; bool ambiguous = false; while(sfd->arg_list && (sfd->name == name || !sfd->name)) { const char *s = sfd->arg_list+1; VDScriptValue *csv = params; bool better_conversion; int current_promotions = 0; // reset current scores to zero (default) memset(current_scores, 0, sizeof(int) * (pcount + 1)); enum { kConversion = -2, kEllipsis = -3 }; for(int i=0; i<pcount; ++i) { const char c = *s++; if (!c) goto arglist_nomatch; switch(c) { case 'v': break; case 'i': if (csv->isLong() || csv->isDouble()) current_scores[i] = kConversion; else if (!csv->isInt()) goto arglist_nomatch; break; case 'l': if (csv->isDouble()) current_scores[i] = kConversion; else if (csv->isInt()) ++current_promotions; else if (!csv->isLong()) goto arglist_nomatch; break; case 'd': if (csv->isInt() || csv->isLong()) ++current_promotions; else if (!csv->isDouble()) goto arglist_nomatch; break; case 's': if (!csv->isString()) goto arglist_nomatch; break; case '.': --s; // repeat this character break; default: SCRIPT_ERROR(EXTERNAL_ERROR); } ++csv; } // check if we have no parms left, or only an ellipsis if (*s == '.' && !s[1]) { current_scores[pcount] = kEllipsis; } else if (*s) goto arglist_nomatch; // do check for better match better_conversion = true; if (sfd_best) { better_conversion = false; for(int i=0; i<=pcount; ++i) { // we check one additional parm, the ellipsis parm // reject if there is a worse conversion than the best match so far if (current_scores[i] < best_scores[i]) { goto arglist_nomatch; } // add +1 if there is a better match if (current_scores[i] > best_scores[i]) better_conversion = true; } // all things being equal, choose the method with fewer promotions if (!better_conversion) { if (current_promotions < best_promotions) better_conversion = true; else if (current_promotions == best_promotions) ambiguous = true; } } if (better_conversion) { std::swap(current_scores, best_scores); sfd_best = sfd; best_promotions = current_promotions; ambiguous = false; } arglist_nomatch: ++sfd; } if (!sfd_best) SCRIPT_ERROR(OVERLOADED_FUNCTION_NOT_FOUND); else if (ambiguous) SCRIPT_ERROR(AMBIGUOUS_CALL); // Make sure there is room for the return value. int stackcount = pcount; if (!stackcount) { ++stackcount; mStack.push_back(VDScriptValue()); } // coerce arguments VDScriptValue *const argv = &*(mStack.end() - stackcount); const char *const argdesc = sfd_best->arg_list + 1; for(int i=0; i<pcount; ++i) { VDScriptValue& a = argv[i]; switch(argdesc[i]) { case 'i': if (a.isLong()) a = VDScriptValue((int)a.asLong()); else if (a.isDouble()) a = VDScriptValue((int)a.asDouble()); break; case 'l': if (a.isInt()) a = VDScriptValue((sint64)a.asInt()); else if (a.isDouble()) a = VDScriptValue((sint64)a.asDouble()); break; case 'd': if (a.isInt()) a = VDScriptValue((double)a.asInt()); else if (a.isLong()) a = VDScriptValue((double)a.asLong()); break; } } // invoke mpCurrentInvocationMethodOverload = sfd_best; sfd_best->func_ptr(this, argv, pcount); mStack.resize(mStack.size() + 1 - stackcount); if (sfd_best->arg_list[0] == '0') mStack.back() = VDScriptValue(); }
void VDScriptInterpreter::ParseValue() { int t = Token(); if (t=='(') { t = Token(); if (t == TOK_INT) { if (Token() != ')') SCRIPT_ERROR(CLOSEPARENS_EXPECTED); ParseExpression(); VDScriptValue& v = mStack.back(); if (v.isDouble()) v = (int)v.asDouble(); else if (v.isLong()) v = (int)v.asLong(); else if (!v.isInt()) SCRIPT_ERROR(CANNOT_CAST); } else if (t == TOK_LONG) { if (Token() != ')') SCRIPT_ERROR(CLOSEPARENS_EXPECTED); ParseExpression(); VDScriptValue& v = mStack.back(); if (v.isDouble()) v = (sint64)v.asDouble(); else if (v.isInt()) v = (sint64)v.asInt(); else if (!v.isLong()) SCRIPT_ERROR(CANNOT_CAST); } else if (t == TOK_DOUBLE) { if (Token() != ')') SCRIPT_ERROR(CLOSEPARENS_EXPECTED); ParseExpression(); VDScriptValue& v = mStack.back(); if (v.isInt()) v = (double)v.asInt(); else if (v.isLong()) v = (double)v.asLong(); else if (!v.isDouble()) SCRIPT_ERROR(CANNOT_CAST); } else { TokenUnput(t); ParseExpression(); if (Token() != ')') SCRIPT_ERROR(CLOSEPARENS_EXPECTED); } } else if (t==TOK_IDENT) { mStack.push_back(LookupRootVariable(szIdent)); } else if (t == TOK_INTVAL) mStack.push_back(VDScriptValue(tokival)); else if (t == TOK_LONGVAL) mStack.push_back(VDScriptValue(toklval)); else if (t == TOK_DBLVAL) mStack.push_back(VDScriptValue(tokdval)); else if (t == TOK_STRING) mStack.push_back(VDScriptValue(tokslit)); else if (t=='!' || t=='~' || t=='-' || t=='+') { ParseValue(); switch(t) { case '!': InvokeMethod(&obj_Sylia, "!", 1); break; case '~': InvokeMethod(&obj_Sylia, "~", 1); break; case '+': InvokeMethod(&obj_Sylia, "+", 1); break; case '-': InvokeMethod(&obj_Sylia, "-", 1); break; break; default: SCRIPT_ERROR(PARSE_ERROR); } } else if (t == TOK_TRUE) mStack.push_back(VDScriptValue(1)); else if (t == TOK_FALSE) mStack.push_back(VDScriptValue(0)); else SCRIPT_ERROR(PARSE_ERROR); }
void VDScriptInterpreter::ParseExpression() { int depth = 0; int t; bool need_value = true; for(;;) { if (need_value) { ParseValue(); need_value = false; } t = Token(); if (!t || t==')' || t==']' || t==',' || t==';') { TokenUnput(t); break; } VDScriptValue& v = mStack.back(); if (t=='.') { // object indirection operator (object -> member) ConvertToRvalue(); if (!v.isObject()) { SCRIPT_ERROR(TYPE_OBJECT_REQUIRED); } if (Token() != TOK_IDENT) SCRIPT_ERROR(OBJECT_MEMBER_NAME_REQUIRED); try { VDScriptValue v2 = LookupObjectMember(v.asObjectDef(), v.asObjectPtr(), szIdent); if (v2.isVoid()) { mErrorObject = v; SCRIPT_ERROR(MEMBER_NOT_FOUND); } v = v2; } catch(const VDScriptError& e) { mErrorObject = v; throw; } } else if (t == '[') { // array indexing operator (object, value -> value) // Reduce lvalues to rvalues ConvertToRvalue(); if (!v.isObject()) SCRIPT_ERROR(TYPE_OBJECT_REQUIRED); ParseExpression(); InvokeMethod((mStack.end() - 2)->asObjectDef(), "[]", 1); VDASSERT(mStack.size() >= 2); mStack.erase(mStack.end() - 2); if (Token() != ']') SCRIPT_ERROR(CLOSEBRACKET_EXPECTED); } else if (t == '(') { // function indirection operator (method -> value) ConvertToRvalue(); // can happen if a method is assigned const VDScriptValue fcall(mStack.back()); mStack.pop_back(); mStack.push_back(VDScriptValue(fcall.u.method.p, fcall.thisPtr)); int pcount = 0; t = Token(); if (t != ')') { TokenUnput(t); for(;;) { ParseExpression(); ++pcount; t = Token(); if (t==')') break; else if (t!=',') SCRIPT_ERROR(FUNCCALLEND_EXPECTED); } } InvokeMethod(fcall.u.method.pfn, pcount); VDASSERT(mStack.size() >= 2); mStack.erase(mStack.end() - 2); } else { int prec = ExprOpPrecedence(t) + ExprOpIsRightAssoc(t); while(depth > 0 && ExprOpPrecedence(mOpStack.back()) >= prec) { --depth; Reduce(); } mOpStack.push_back(t); ++depth; need_value = true; } } // reduce until no ops are left while(depth-- > 0) Reduce(); }