home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Sylia / ScriptInterpreter.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2009-09-14  |  25.9 KB  |  1,124 lines

  1. #include <vd2/system/vdtypes.h>
  2. #include <vd2/system/text.h>
  3. #include <vd2/system/VDString.h>
  4. #include <vd2/system/vdstl.h>
  5. #include <vector>
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <ctype.h>
  10. #include <malloc.h>
  11.  
  12. #include "StringHeap.h"
  13. #include "ScriptError.h"
  14. #include "ScriptValue.h"
  15. #include "ScriptInterpreter.h"
  16. #include "VariableTable.h"
  17.  
  18. ///////////////////////////////////////////////////////////////////////////
  19.  
  20. //#define DEBUG_TOKEN
  21.  
  22. ///////////////////////////////////////////////////////////////////////////
  23.  
  24. extern VDScriptObject obj_Sylia;
  25.  
  26. ///////////////////////////////////////////////////////////////////////////
  27.  
  28. class VDScriptInterpreter : public IVDScriptInterpreter {
  29. private:
  30.     enum {
  31.         MAX_FUNCTION_PARAMS = 16,
  32.         MAX_IDENT_CHARS = 64
  33.     };
  34.  
  35.     enum {
  36.         TOK_IDENT        = 256,
  37.         TOK_INTVAL,
  38.         TOK_LONGVAL,
  39.         TOK_DBLVAL,
  40.         TOK_INT,
  41.         TOK_LONG,
  42.         TOK_DOUBLE,
  43.         TOK_STRING,
  44.         TOK_DECLARE,
  45.         TOK_TRUE,
  46.         TOK_FALSE,
  47.         TOK_AND,
  48.         TOK_OR,
  49.         TOK_EQUALS,
  50.         TOK_NOTEQ,
  51.         TOK_LESSEQ,
  52.         TOK_GRTREQ,
  53.     };
  54.  
  55.     const char *tokbase;
  56.     const char *tokstr;
  57.     union {
  58.         int tokival;
  59.         sint64 toklval;
  60.         double tokdval;
  61.     };
  62.     int tokhold;
  63.     char **tokslit;
  64.     char szIdent[MAX_IDENT_CHARS+1];
  65.     VDScriptValue        mErrorObject;
  66.     vdfastvector<char> mError;
  67.  
  68.     std::vector<VDScriptValue> mStack;
  69.     vdfastvector<int> mOpStack;
  70.  
  71.     VDScriptRootHandlerPtr lpRoothandler;
  72.     void *lpRoothandlerData;
  73.  
  74.     VDScriptStringHeap strheap;
  75.     VariableTable    vartbl;
  76.  
  77.     const VDScriptFunctionDef *mpCurrentInvocationMethod;
  78.     const VDScriptFunctionDef *mpCurrentInvocationMethodOverload;
  79.     int mMethodArgumentCount;
  80.  
  81.     VDStringA mErrorExtraToken;
  82.  
  83.     ////////
  84.  
  85.     void            ParseExpression();
  86.     int                ExprOpPrecedence(int op);
  87.     bool            ExprOpIsRightAssoc(int op);
  88.     void            ParseValue();
  89.     void            Reduce();
  90.  
  91.     void    ConvertToRvalue();
  92.     void    InvokeMethod(const VDScriptObject *objdef, const char *name, int argc);
  93.     void    InvokeMethod(const VDScriptFunctionDef *sfd, int pcount);
  94.  
  95.     bool    isExprFirstToken(int t);
  96.     bool    isUnaryToken(int t);
  97.  
  98.     VDScriptValue    LookupRootVariable(char *szName);
  99.  
  100.     bool isIdentFirstChar(char c);
  101.     bool isIdentNextChar(char c);
  102.     void TokenBegin(const char *s);
  103.     void TokenUnput(int t);
  104.     int Token();
  105.     void GC();
  106.  
  107. public:
  108.     VDScriptInterpreter();
  109.     ~VDScriptInterpreter();
  110.     void Destroy();
  111.  
  112.     void SetRootHandler(VDScriptRootHandlerPtr, void *);
  113.  
  114.     void ExecuteLine(const char *s);
  115.  
  116.     void ScriptError(int e);
  117.     const char *TranslateScriptError(const VDScriptError& cse);
  118.     char** AllocTempString(long l);
  119.     VDScriptValue    DupCString(const char *s);
  120.  
  121.     VDScriptValue    LookupObjectMember(const VDScriptObject *obj, void *lpVoid, char *szIdent);
  122.  
  123.     const VDScriptFunctionDef *GetCurrentMethod() { return mpCurrentInvocationMethodOverload; }
  124.     int GetErrorLocation() { return tokstr - tokbase; }
  125. };
  126.  
  127. ///////////////////////////////////////////////////////////////////////////
  128.  
  129. VDScriptInterpreter::VDScriptInterpreter() : vartbl(128) {
  130. }
  131.  
  132. VDScriptInterpreter::~VDScriptInterpreter() {
  133.     lpRoothandler = NULL;
  134. }
  135.  
  136. void VDScriptInterpreter::Destroy() {
  137.     delete this;
  138. }
  139.  
  140. IVDScriptInterpreter *VDCreateScriptInterpreter() {
  141.     return new VDScriptInterpreter();
  142. }
  143.  
  144. ///////////////////////////////////////////////////////////////////////////
  145.  
  146. void VDScriptInterpreter::SetRootHandler(VDScriptRootHandlerPtr rh, void *rh_data) {
  147.     lpRoothandler = rh;
  148.     lpRoothandlerData = rh_data;
  149. }
  150.  
  151. ///////////////////////////////////////////////////////////////////////////
  152.  
  153. void VDScriptInterpreter::ExecuteLine(const char *s) {
  154.     int t;
  155.  
  156.     mErrorExtraToken.clear();
  157.  
  158.     TokenBegin(s);
  159.  
  160.     while(t = Token()) {
  161.         if (isExprFirstToken(t)) {
  162.             TokenUnput(t);
  163.             VDASSERT(mStack.empty());
  164.             ParseExpression();
  165.  
  166.             VDASSERT(!mStack.empty());
  167.  
  168.             VDScriptValue& val = mStack.back();
  169.  
  170.             mStack.pop_back();
  171.             VDASSERT(mStack.empty());
  172.  
  173.             if (Token() != ';')
  174.                 SCRIPT_ERROR(SEMICOLON_EXPECTED);
  175.         } else if (t == TOK_DECLARE) {
  176.  
  177.             do {
  178.                 t = Token();
  179.  
  180.                 if (t != TOK_IDENT)
  181.                     SCRIPT_ERROR(IDENTIFIER_EXPECTED);
  182.  
  183.                 VariableTableEntry *vte = vartbl.Declare(szIdent);
  184.  
  185.                 t = Token();
  186.  
  187.                 if (t == '=') {
  188.                     ParseExpression();
  189.  
  190.                     VDASSERT(!mStack.empty());
  191.                     vte->v = mStack.back();
  192.                     mStack.pop_back();
  193.  
  194.                     t = Token();
  195.                 }
  196.  
  197.             } while(t == ',');
  198.  
  199.             if (t != ';')
  200.                 SCRIPT_ERROR(SEMICOLON_EXPECTED);
  201.  
  202.         } else
  203.             SCRIPT_ERROR(PARSE_ERROR);
  204.     }
  205.  
  206.     VDASSERT(mStack.empty());
  207.  
  208.     GC();
  209. }
  210.  
  211. void VDScriptInterpreter::ScriptError(int e) {
  212.     throw VDScriptError(e);
  213. }
  214.  
  215. namespace {
  216.     void strveccat(vdfastvector<char>& error, const char *s) {
  217.         error.insert(error.end(), s, s+strlen(s));
  218.     }
  219. }
  220.  
  221. const char *VDScriptInterpreter::TranslateScriptError(const VDScriptError& cse) {
  222.     int i;
  223.     char szError[1024];
  224.  
  225.     switch(cse.err) {
  226.     case VDScriptError::OVERLOADED_FUNCTION_NOT_FOUND:
  227.     case VDScriptError::AMBIGUOUS_CALL:
  228.         {
  229.             if (cse.err == VDScriptError::OVERLOADED_FUNCTION_NOT_FOUND) {
  230.                 if (mpCurrentInvocationMethod && mpCurrentInvocationMethod->name)
  231.                     sprintf(szError, "Overloaded method %s(", mpCurrentInvocationMethod->name);
  232.                 else
  233.                     strcpy(szError, "Overloaded method (");
  234.             } else {
  235.                 if (mpCurrentInvocationMethod && mpCurrentInvocationMethod->name)
  236.                     sprintf(szError, "Ambiguous call with arguments %s(", mpCurrentInvocationMethod->name);
  237.                 else
  238.                     strcpy(szError, "Ambiguous call with arguments (");
  239.             }
  240.  
  241.             mError.assign(szError, szError + strlen(szError));
  242.  
  243.             if (mMethodArgumentCount) {
  244.                 const VDScriptValue *const argv = &*(mStack.end() - mMethodArgumentCount);
  245.  
  246.                 for(i=0; i<mMethodArgumentCount; i++) {
  247.                     if (i) {
  248.                         if (argv[i].isVoid()) break;
  249.  
  250.                         mError.push_back(',');
  251.                     }
  252.  
  253.                     switch(argv[i].type) {
  254.                     case VDScriptValue::T_VOID:        strveccat(mError, "void"); break;
  255.                     case VDScriptValue::T_INT:        strveccat(mError, "int"); break;
  256.                     case VDScriptValue::T_LONG:        strveccat(mError, "long"); break;
  257.                     case VDScriptValue::T_DOUBLE:        strveccat(mError, "double"); break;
  258.                     case VDScriptValue::T_STR:        strveccat(mError, "string"); break;
  259.                     case VDScriptValue::T_OBJECT:    strveccat(mError, "object"); break;
  260.                     case VDScriptValue::T_FNAME:        strveccat(mError, "method"); break;
  261.                     case VDScriptValue::T_FUNCTION:    strveccat(mError, "function"); break;
  262.                     case VDScriptValue::T_VARLV:        strveccat(mError, "var"); break;
  263.                     }
  264.                 }
  265.             }
  266.  
  267.             strveccat(mError, ")");
  268.             if (cse.err == VDScriptError::OVERLOADED_FUNCTION_NOT_FOUND)
  269.                 strveccat(mError, " not found");
  270.         }
  271.         mError.push_back(0);
  272.  
  273.         return mError.data();
  274.     case VDScriptError::MEMBER_NOT_FOUND:
  275.         sprintf(szError, "Class '%s' does not have a member called '%s'", mErrorObject.asObjectDef()->mpName, szIdent);
  276.         mError.assign(szError, szError + strlen(szError) + 1);
  277.         return mError.data();
  278.     case VDScriptError::VAR_NOT_FOUND:
  279.         if (!mErrorExtraToken.empty()) {
  280.             sprintf(szError, "Variable '%s' not found", mErrorExtraToken.c_str());
  281.             mError.assign(szError, szError + strlen(szError) + 1);
  282.             return mError.data();
  283.         }
  284.         break;
  285.     }
  286.     return ::VDScriptTranslateError(cse);
  287. }
  288.  
  289. char **VDScriptInterpreter::AllocTempString(long l) {
  290.     char **handle = strheap.Allocate(l);
  291.  
  292.     (*handle)[l]=0;
  293.  
  294.     return handle;
  295. }
  296.  
  297. VDScriptValue VDScriptInterpreter::DupCString(const char *s) {
  298.     const size_t l = strlen(s);
  299.     char **pp = AllocTempString(l);
  300.  
  301.     memcpy(*pp, s, l);
  302.     return VDScriptValue(pp);
  303. }
  304.  
  305. ///////////////////////////////////////////////////////////////////////////
  306. //
  307. //    Expression parsing
  308. //
  309. ///////////////////////////////////////////////////////////////////////////
  310.  
  311. int VDScriptInterpreter::ExprOpPrecedence(int op) {
  312.     // All of these need to be EVEN.
  313.     switch(op) {
  314.     case '=':            return 2;
  315.     case TOK_OR:        return 4;
  316.     case TOK_AND:        return 6;
  317.     case '|':            return 8;
  318.     case '^':            return 10;
  319.     case '&':            return 12;
  320.     case TOK_EQUALS:
  321.     case TOK_NOTEQ:        return 14;
  322.     case '<':
  323.     case '>':
  324.     case TOK_LESSEQ:
  325.     case TOK_GRTREQ:    return 16;
  326.     case '+':
  327.     case '-':            return 18;
  328.     case '*':
  329.     case '/':
  330.     case '%':            return 20;
  331.     case '.':            return 22;
  332.     }
  333.  
  334.     return 0;
  335. }
  336.  
  337. bool VDScriptInterpreter::ExprOpIsRightAssoc(int op) {
  338.     return op == '=';
  339. }
  340.  
  341. void VDScriptInterpreter::ParseExpression() {
  342.     int depth = 0;
  343.     int t;
  344.     bool need_value = true;
  345.  
  346.     for(;;) {
  347.         if (need_value) {
  348.             ParseValue();
  349.             need_value = false;
  350.         }
  351.  
  352.         t = Token();
  353.  
  354.         if (!t || t==')' || t==']' || t==',' || t==';') {
  355.             TokenUnput(t);
  356.             break;
  357.         }
  358.  
  359.         VDScriptValue& v = mStack.back();
  360.  
  361.         if (t=='.') {            // object indirection operator (object -> member)
  362.             ConvertToRvalue();
  363.  
  364.             if (!v.isObject()) {
  365.                 SCRIPT_ERROR(TYPE_OBJECT_REQUIRED);
  366.             }
  367.  
  368.             if (Token() != TOK_IDENT)
  369.                 SCRIPT_ERROR(OBJECT_MEMBER_NAME_REQUIRED);
  370.  
  371.             try {
  372.                 VDScriptValue v2 = LookupObjectMember(v.asObjectDef(), v.asObjectPtr(), szIdent);
  373.  
  374.                 if (v2.isVoid()) {
  375.                     mErrorObject = v;
  376.                     SCRIPT_ERROR(MEMBER_NOT_FOUND);
  377.                 }
  378.  
  379.                 v = v2;
  380.             } catch(const VDScriptError&) {
  381.                 mErrorObject = v;
  382.                 throw;
  383.             }
  384.  
  385.         } else if (t == '[') {    // array indexing operator (object, value -> value)
  386.             // Reduce lvalues to rvalues
  387.  
  388.             ConvertToRvalue();
  389.  
  390.             if (!v.isObject())
  391.                 SCRIPT_ERROR(TYPE_OBJECT_REQUIRED);
  392.  
  393.             ParseExpression();
  394.             InvokeMethod((mStack.end() - 2)->asObjectDef(), "[]", 1);
  395.  
  396.             VDASSERT(mStack.size() >= 2);
  397.             mStack.erase(mStack.end() - 2);
  398.  
  399.             if (Token() != ']')
  400.                 SCRIPT_ERROR(CLOSEBRACKET_EXPECTED);
  401.         } else if (t == '(') {    // function indirection operator (method -> value)
  402.             ConvertToRvalue();    // can happen if a method is assigned
  403.  
  404.             const VDScriptValue fcall(mStack.back());
  405.             mStack.pop_back();
  406.  
  407.             mStack.push_back(VDScriptValue(fcall.u.method.p, fcall.thisPtr));
  408.  
  409.             int pcount = 0;
  410.  
  411.             t = Token();
  412.             if (t != ')') {
  413.                 TokenUnput(t);
  414.  
  415.                 for(;;) {
  416.                     ParseExpression();
  417.                     ++pcount;
  418.                     
  419.                     t = Token();
  420.  
  421.                     if (t==')')
  422.                         break;
  423.                     else if (t!=',')
  424.                         SCRIPT_ERROR(FUNCCALLEND_EXPECTED);
  425.                 }
  426.             }
  427.  
  428.             InvokeMethod(fcall.u.method.pfn, pcount);
  429.  
  430.             VDASSERT(mStack.size() >= 2);
  431.             mStack.erase(mStack.end() - 2);
  432.         } else {
  433.             int prec = ExprOpPrecedence(t) + ExprOpIsRightAssoc(t);
  434.  
  435.             while(depth > 0 && ExprOpPrecedence(mOpStack.back()) >= prec) {
  436.                 --depth;
  437.                 Reduce();
  438.             }
  439.  
  440.             mOpStack.push_back(t);
  441.             ++depth;
  442.  
  443.             need_value = true;
  444.         }
  445.     }
  446.  
  447.     // reduce until no ops are left
  448.     while(depth-- > 0)
  449.         Reduce();
  450. }
  451.  
  452. void VDScriptInterpreter::Reduce() {
  453.     const int op = mOpStack.back();
  454.     mOpStack.pop_back();
  455.  
  456.     switch(op) {
  457.     case '=':
  458.         {
  459.             VDScriptValue& v = mStack[mStack.size() - 2];
  460.  
  461.             if (!v.isVarLV())
  462.                 SCRIPT_ERROR(TYPE_OBJECT_REQUIRED);
  463.  
  464.             ConvertToRvalue();
  465.  
  466.             v.asVarLV()->v = mStack.back();
  467.             mStack.pop_back();
  468.         }
  469.         break;
  470.     case TOK_OR:        InvokeMethod(&obj_Sylia, "||", 2); break;
  471.     case TOK_AND:        InvokeMethod(&obj_Sylia, "&&", 2); break;
  472.     case '|':            InvokeMethod(&obj_Sylia, "|", 2); break;
  473.     case '^':            InvokeMethod(&obj_Sylia, "^", 2); break;
  474.     case '&':            InvokeMethod(&obj_Sylia, "&", 2); break;
  475.     case TOK_EQUALS:    InvokeMethod(&obj_Sylia, "==", 2); break;
  476.     case TOK_NOTEQ:        InvokeMethod(&obj_Sylia, "!=", 2); break;
  477.     case '<':            InvokeMethod(&obj_Sylia, "<", 2); break;
  478.     case '>':            InvokeMethod(&obj_Sylia, ">", 2); break;
  479.     case TOK_LESSEQ:    InvokeMethod(&obj_Sylia, "<=", 2); break;
  480.     case TOK_GRTREQ:    InvokeMethod(&obj_Sylia, ">=", 2); break;
  481.     case '+':            InvokeMethod(&obj_Sylia, "+", 2); break;
  482.     case '-':            InvokeMethod(&obj_Sylia, "-", 2); break;
  483.     case '*':            InvokeMethod(&obj_Sylia, "*", 2); break;
  484.     case '/':            InvokeMethod(&obj_Sylia, "/", 2); break;
  485.     case '%':            InvokeMethod(&obj_Sylia, "%", 2); break;
  486.     }
  487. }
  488.  
  489. void VDScriptInterpreter::ParseValue() {
  490.     int t = Token();
  491.  
  492.     if (t=='(') {
  493.         t = Token();
  494.  
  495.         if (t == TOK_INT) {
  496.             if (Token() != ')')
  497.                 SCRIPT_ERROR(CLOSEPARENS_EXPECTED);
  498.  
  499.             ParseExpression();
  500.  
  501.             VDScriptValue& v = mStack.back();
  502.  
  503.             if (v.isDouble())
  504.                 v = (int)v.asDouble();
  505.             else if (v.isLong())
  506.                 v = (int)v.asLong();
  507.             else if (!v.isInt())
  508.                 SCRIPT_ERROR(CANNOT_CAST);
  509.         } else if (t == TOK_LONG) {
  510.             if (Token() != ')')
  511.                 SCRIPT_ERROR(CLOSEPARENS_EXPECTED);
  512.  
  513.             ParseExpression();
  514.  
  515.             VDScriptValue& v = mStack.back();
  516.  
  517.             if (v.isDouble())
  518.                 v = (sint64)v.asDouble();
  519.             else if (v.isInt())
  520.                 v = (sint64)v.asInt();
  521.             else if (!v.isLong())
  522.                 SCRIPT_ERROR(CANNOT_CAST);
  523.         } else if (t == TOK_DOUBLE) {
  524.             if (Token() != ')')
  525.                 SCRIPT_ERROR(CLOSEPARENS_EXPECTED);
  526.  
  527.             ParseExpression();
  528.  
  529.             VDScriptValue& v = mStack.back();
  530.  
  531.             if (v.isInt())
  532.                 v = (double)v.asInt();
  533.             else if (v.isLong())
  534.                 v = (double)v.asLong();
  535.             else if (!v.isDouble())
  536.                 SCRIPT_ERROR(CANNOT_CAST);
  537.         } else {
  538.             TokenUnput(t);
  539.  
  540.             ParseExpression();
  541.  
  542.             if (Token() != ')')
  543.                 SCRIPT_ERROR(CLOSEPARENS_EXPECTED);
  544.         }
  545.     } else if (t==TOK_IDENT) {
  546.         mStack.push_back(LookupRootVariable(szIdent));
  547.     } else if (t == TOK_INTVAL)
  548.         mStack.push_back(VDScriptValue(tokival));
  549.     else if (t == TOK_LONGVAL)
  550.         mStack.push_back(VDScriptValue(toklval));
  551.     else if (t == TOK_DBLVAL)
  552.         mStack.push_back(VDScriptValue(tokdval));
  553.     else if (t == TOK_STRING)
  554.         mStack.push_back(VDScriptValue(tokslit));
  555.     else if (t=='!' || t=='~' || t=='-' || t=='+') {
  556.         ParseValue();
  557.  
  558.         switch(t) {
  559.         case '!':        InvokeMethod(&obj_Sylia, "!", 1); break;
  560.         case '~':        InvokeMethod(&obj_Sylia, "~", 1); break;
  561.         case '+':        InvokeMethod(&obj_Sylia, "+", 1); break;
  562.         case '-':        InvokeMethod(&obj_Sylia, "-", 1); break;
  563.             break;
  564.         default:
  565.             SCRIPT_ERROR(PARSE_ERROR);
  566.         }
  567.     } else if (t == TOK_TRUE)
  568.         mStack.push_back(VDScriptValue(1));
  569.     else if (t == TOK_FALSE)
  570.         mStack.push_back(VDScriptValue(0));
  571.     else
  572.         SCRIPT_ERROR(PARSE_ERROR);
  573. }
  574.  
  575.  
  576. ///////////////////////////////////////////////////////////////////////////
  577. //
  578. //    Variables...
  579. //
  580. ///////////////////////////////////////////////////////////////////////////
  581.  
  582. void VDScriptInterpreter::InvokeMethod(const VDScriptObject *obj, const char *name, int argc) {
  583.     if (obj->func_list) {
  584.         const VDScriptFunctionDef *sfd = obj->func_list;
  585.  
  586.         while(sfd->arg_list) {
  587.             if (sfd->name && !strcmp(sfd->name, name)) {
  588.                 InvokeMethod(sfd, argc);
  589.                 return;
  590.             }
  591.  
  592.             ++sfd;
  593.         }
  594.     }
  595.  
  596.     SCRIPT_ERROR(OVERLOADED_FUNCTION_NOT_FOUND);
  597. }
  598.  
  599. void VDScriptInterpreter::InvokeMethod(const VDScriptFunctionDef *sfd, int pcount) {
  600.     VDScriptValue *params = NULL;
  601.     
  602.     if (pcount)
  603.         params = &mStack[mStack.size() - pcount];
  604.  
  605.     // convert params to rvalues
  606.     for(int j=0; j<pcount; ++j) {
  607.         VDScriptValue& parm = params[j];
  608.  
  609.         if (parm.isVarLV())
  610.             parm = parm.asVarLV()->v;
  611.     }
  612.  
  613.     mpCurrentInvocationMethod = sfd;
  614.     mMethodArgumentCount = pcount;
  615.  
  616.     // If we were passed a function name, attempt to match our parameter
  617.     // list to one of the overloaded function templates.
  618.     //
  619.     // 0 = void, v = value, i = int, . = varargs
  620.     //
  621.     // <return value><param1><param2>...
  622.     const char *name = sfd->name;
  623.  
  624.     // Yes, goto's are usually considered gross... but I prefer
  625.     // cleanly labeled goto's to excessive boolean variable usage.
  626.     const VDScriptFunctionDef *sfd_best = NULL;
  627.  
  628.     int *best_scores = (int *)_alloca(sizeof(int) * (pcount + 1));
  629.     int *current_scores = (int *)_alloca(sizeof(int) * (pcount + 1));
  630.     int best_promotions = 0;
  631.     bool ambiguous = false;
  632.  
  633.     while(sfd->arg_list && (sfd->name == name || !sfd->name)) {
  634.         const char *s = sfd->arg_list+1;
  635.         VDScriptValue *csv = params;
  636.         bool better_conversion;
  637.         int current_promotions = 0;
  638.  
  639.         // reset current scores to zero (default)
  640.         memset(current_scores, 0, sizeof(int) * (pcount + 1));
  641.  
  642.         enum {
  643.             kConversion = -2,
  644.             kEllipsis = -3
  645.         };
  646.  
  647.         for(int i=0; i<pcount; ++i) {
  648.             const char c = *s++;
  649.  
  650.             if (!c)
  651.                 goto arglist_nomatch;
  652.  
  653.             switch(c) {
  654.             case 'v':
  655.                 break;
  656.             case 'i':
  657.                 if (csv->isLong() || csv->isDouble())
  658.                     current_scores[i] = kConversion;
  659.                 else if (!csv->isInt())
  660.                     goto arglist_nomatch;
  661.                 break;
  662.             case 'l':
  663.                 if (csv->isDouble())
  664.                     current_scores[i] = kConversion;
  665.                 else if (csv->isInt())
  666.                     ++current_promotions;
  667.                 else if (!csv->isLong())
  668.                     goto arglist_nomatch;
  669.                 break;
  670.             case 'd':
  671.                 if (csv->isInt() || csv->isLong())
  672.                     ++current_promotions;
  673.                 else if (!csv->isDouble())
  674.                     goto arglist_nomatch;
  675.                 break;
  676.             case 's':
  677.                 if (!csv->isString()) goto arglist_nomatch;
  678.                 break;
  679.             case '.':
  680.                 --s;            // repeat this character
  681.                 break;
  682.             default:
  683.                 SCRIPT_ERROR(EXTERNAL_ERROR);
  684.             }
  685.             ++csv;
  686.         }
  687.  
  688.         // check if we have no parms left, or only an ellipsis
  689.         if (*s == '.' && !s[1]) {
  690.             current_scores[pcount] = kEllipsis;
  691.         } else if (*s)
  692.             goto arglist_nomatch;
  693.  
  694.         // do check for better match
  695.         better_conversion = true;
  696.  
  697.         if (sfd_best) {
  698.             better_conversion = false;
  699.             for(int i=0; i<=pcount; ++i) {        // we check one additional parm, the ellipsis parm
  700.                 // reject if there is a worse conversion than the best match so far
  701.                 if (current_scores[i] < best_scores[i]) {
  702.                     goto arglist_nomatch;
  703.                 }
  704.  
  705.                 // add +1 if there is a better match
  706.                 if (current_scores[i] > best_scores[i])
  707.                     better_conversion = true;
  708.             }
  709.  
  710.             // all things being equal, choose the method with fewer promotions
  711.             if (!better_conversion) {
  712.                 if (current_promotions < best_promotions)
  713.                     better_conversion = true;
  714.                 else if (current_promotions == best_promotions)
  715.                     ambiguous = true;
  716.             }
  717.         }
  718.  
  719.         if (better_conversion) {
  720.             std::swap(current_scores, best_scores);
  721.             sfd_best = sfd;
  722.             best_promotions = current_promotions;
  723.             ambiguous = false;
  724.         }
  725.  
  726. arglist_nomatch:
  727.         ++sfd;
  728.     }
  729.  
  730.     if (!sfd_best)
  731.         SCRIPT_ERROR(OVERLOADED_FUNCTION_NOT_FOUND);
  732.     else if (ambiguous)
  733.         SCRIPT_ERROR(AMBIGUOUS_CALL);
  734.  
  735.     // Make sure there is room for the return value.
  736.     int stackcount = pcount;
  737.  
  738.     if (!stackcount) {
  739.         ++stackcount;
  740.         mStack.push_back(VDScriptValue());
  741.     }
  742.  
  743.     // coerce arguments
  744.     VDScriptValue *const argv = &*(mStack.end() - stackcount);
  745.     const char *const argdesc = sfd_best->arg_list + 1;
  746.  
  747.     for(int i=0; i<pcount; ++i) {
  748.         VDScriptValue& a = argv[i];
  749.  
  750.         if (argdesc[i] == '.')
  751.             break;
  752.  
  753.         switch(argdesc[i]) {
  754.         case 'i':
  755.             if (a.isLong())
  756.                 a = VDScriptValue((int)a.asLong());
  757.             else if (a.isDouble())
  758.                 a = VDScriptValue((int)a.asDouble());
  759.             break;
  760.         case 'l':
  761.             if (a.isInt())
  762.                 a = VDScriptValue((sint64)a.asInt());
  763.             else if (a.isDouble())
  764.                 a = VDScriptValue((sint64)a.asDouble());
  765.             break;
  766.         case 'd':
  767.             if (a.isInt())
  768.                 a = VDScriptValue((double)a.asInt());
  769.             else if (a.isLong())
  770.                 a = VDScriptValue((double)a.asLong());
  771.             break;
  772.         }
  773.     }
  774.  
  775.     // invoke
  776.     mpCurrentInvocationMethodOverload = sfd_best;
  777.     sfd_best->func_ptr(this, argv, pcount);
  778.     mStack.resize(mStack.size() + 1 - stackcount);
  779.     if (sfd_best->arg_list[0] == '0')
  780.         mStack.back() = VDScriptValue();
  781. }
  782.  
  783. VDScriptValue VDScriptInterpreter::LookupRootVariable(char *szName) {
  784.     VariableTableEntry *vte;
  785.  
  786.     if (vte = vartbl.Lookup(szName))
  787.         return VDScriptValue(vte);
  788.  
  789.     if (!strcmp(szName, "Sylia"))
  790.         return VDScriptValue(NULL, &obj_Sylia);
  791.  
  792.     const char *volatile _szName = szName;        // needed to fix exception handler, for some reason
  793.  
  794.     VDScriptValue ret;
  795.  
  796.     try {
  797.         if (!lpRoothandler)
  798.             SCRIPT_ERROR(VAR_NOT_FOUND);
  799.  
  800.         ret = lpRoothandler(this, szName, lpRoothandlerData);
  801.     } catch(const VDScriptError& e) {
  802.         if (e.err == VDScriptError::VAR_NOT_FOUND) {
  803.             mErrorExtraToken = _szName;
  804.             throw;
  805.         }
  806.     }
  807.  
  808.     return ret;
  809. }
  810.  
  811. VDScriptValue VDScriptInterpreter::LookupObjectMember(const VDScriptObject *obj, void *lpVoid, char *szIdent) {
  812.     for(; obj; obj = obj->pNextObject) {
  813.         if (obj->func_list) {
  814.             const VDScriptFunctionDef *pfd = obj->func_list;
  815.  
  816.             for(; pfd->func_ptr; ++pfd) {
  817.                 if (pfd->name && !strcmp(pfd->name, szIdent))
  818.                     return VDScriptValue(lpVoid, obj, pfd);
  819.             }
  820.         }
  821.  
  822.         if (obj->obj_list) {
  823.             const VDScriptObjectDef *sod = obj->obj_list;
  824.  
  825.             while(sod->name) {
  826.                 if (!strcmp(sod->name, szIdent)) {
  827.                     VDScriptValue t(lpVoid, sod->obj);
  828.                     return t;
  829.                 }
  830.  
  831.                 ++sod;
  832.             }
  833.         }
  834.  
  835.         if (obj->Lookup) {
  836.             VDScriptValue v(obj->Lookup(this, obj, lpVoid, szIdent));
  837.             if (!v.isVoid())
  838.                 return v;
  839.         }
  840.     }
  841.  
  842.     return VDScriptValue();
  843. }
  844.  
  845. void VDScriptInterpreter::ConvertToRvalue() {
  846.     VDASSERT(!mStack.empty());
  847.  
  848.     VDScriptValue& val = mStack.back();
  849.  
  850.     if (val.isVarLV())
  851.         val = val.asVarLV()->v;
  852. }
  853.  
  854. ///////////////////////////////////////////////////////////////////////////
  855. //
  856. //    Token-level parsing
  857. //
  858. ///////////////////////////////////////////////////////////////////////////
  859.  
  860. bool VDScriptInterpreter::isExprFirstToken(int t) {
  861.     return t==TOK_IDENT || t==TOK_STRING || t==TOK_INTVAL || isUnaryToken(t) || t=='(';
  862. }
  863.  
  864. bool VDScriptInterpreter::isUnaryToken(int t) {
  865.     return t=='+' || t=='-' || t=='!' || t=='~';
  866. }
  867.  
  868. ///////////////////////////////////////////////////////////////////////////
  869. //
  870. //    Character-level parsing
  871. //
  872. ///////////////////////////////////////////////////////////////////////////
  873.  
  874. bool VDScriptInterpreter::isIdentFirstChar(char c) {
  875.     return isalpha((unsigned char)c) || c=='_';
  876. }
  877.  
  878. bool VDScriptInterpreter::isIdentNextChar(char c) {
  879.     return isalnum((unsigned char)c) || c=='_';
  880. }
  881.  
  882. void VDScriptInterpreter::TokenBegin(const char *s) {
  883.     tokbase = tokstr = s;
  884.     tokhold = 0;
  885. }
  886.  
  887. void VDScriptInterpreter::TokenUnput(int t) {
  888.     tokhold = t;
  889. }
  890.  
  891. int VDScriptInterpreter::Token() {
  892.     static char hexdig[]="0123456789ABCDEF";
  893.     char *s,c;
  894.  
  895.     if (tokhold) {
  896.         int t = tokhold;
  897.         tokhold = 0;
  898.         return t;
  899.     }
  900.  
  901.     do {
  902.         c=*tokstr++;
  903.     } while(c && isspace((unsigned char)c));
  904.  
  905.     if (!c) {
  906.         --tokstr;
  907.  
  908.         return 0;
  909.     }
  910.  
  911.     // C++ style comment?
  912.  
  913.     if (c=='/')
  914.         if (tokstr[0]=='/') {
  915.             while(*tokstr) ++tokstr;
  916.  
  917.             return 0;        // C++ comment
  918.         } else
  919.             return '/';
  920.  
  921.     // string?
  922.  
  923.     if (c=='"') {
  924.         const char *s = tokstr;
  925.         char *t;
  926.         long len_adjust=0;
  927.  
  928.         while((c=*tokstr++) && c!='"') {
  929.             if (c=='\\') {
  930.                 c = *tokstr++;
  931.                 if (!c) SCRIPT_ERROR(PARSE_ERROR);
  932.                 else {
  933.                     if (c=='x') {
  934.                         if (!isxdigit((unsigned char)tokstr[0]) || !isxdigit((unsigned char)tokstr[1]))
  935.                             SCRIPT_ERROR(PARSE_ERROR);
  936.                         tokstr+=2;
  937.                         len_adjust += 2;
  938.                     }
  939.                     ++len_adjust;
  940.                 }
  941.             }
  942.         }
  943.  
  944.         tokslit = strheap.Allocate(tokstr - s - 1 - len_adjust);
  945.         t = *tokslit;
  946.         while(s<tokstr-1) {
  947.             int val;
  948.  
  949.             c = *s++;
  950.  
  951.             if (c=='\\')
  952.                 switch(c=*s++) {
  953.                 case 'a': *t++='\a'; break;
  954.                 case 'b': *t++='\b'; break;
  955.                 case 'f': *t++='\f'; break;
  956.                 case 'n': *t++='\n'; break;
  957.                 case 'r': *t++='\r'; break;
  958.                 case 't': *t++='\t'; break;
  959.                 case 'v': *t++='\v'; break;
  960.                 case 'x':
  961.                     val = strchr(hexdig,toupper(s[0]))-hexdig;
  962.                     val = (val<<4) | (strchr(hexdig,toupper(s[1]))-hexdig);
  963.                     *t++ = val;
  964.                     s += 2;
  965.                     break;
  966.                 default:
  967.                     *t++ = c;
  968.                 }
  969.             else
  970.                 *t++ = c;
  971.         }
  972.         *t=0;
  973.  
  974.         if (!c) --tokstr;
  975.  
  976.         return TOK_STRING;
  977.     }
  978.  
  979.     // unescaped string?
  980.     if ((c=='u' || c=='U') && *tokstr == '"') {
  981.         const char *s = ++tokstr;
  982.  
  983.         while((c=*tokstr++) && c != '"')
  984.             ;
  985.  
  986.         if (!c) {
  987.             --tokstr;
  988.             SCRIPT_ERROR(PARSE_ERROR);
  989.         }
  990.  
  991.         size_t len = tokstr - s - 1;
  992.  
  993.         const VDStringA strA(VDTextWToU8(VDTextAToW(s, len)));
  994.  
  995.         len = strA.size();
  996.  
  997.         tokslit = strheap.Allocate(len);
  998.         memcpy(*tokslit, strA.data(), len);
  999.         (*tokslit)[len] = 0;
  1000.  
  1001.         return TOK_STRING;
  1002.     }
  1003.  
  1004.     // look for variable/keyword
  1005.  
  1006.     if (isIdentFirstChar(c)) {
  1007.         s = szIdent;
  1008.  
  1009.         *s++ = c;
  1010.         while(isIdentNextChar(c = *tokstr++)) {
  1011.             if (s>=szIdent + MAX_IDENT_CHARS)
  1012.                 SCRIPT_ERROR(IDENT_TOO_LONG);
  1013.  
  1014.             *s++ = c;
  1015.         }
  1016.  
  1017.         --tokstr;
  1018.         *s=0;
  1019.  
  1020.         if (!strcmp(szIdent, "declare"))
  1021.             return TOK_DECLARE;
  1022.         else if (!strcmp(szIdent, "true"))
  1023.             return TOK_TRUE;
  1024.         else if (!strcmp(szIdent, "false"))
  1025.             return TOK_FALSE;
  1026.         else if (!strcmp(szIdent, "int"))
  1027.             return TOK_INT;
  1028.         else if (!strcmp(szIdent, "long"))
  1029.             return TOK_LONG;
  1030.         else if (!strcmp(szIdent, "double"))
  1031.             return TOK_DOUBLE;
  1032.  
  1033.         return TOK_IDENT;
  1034.     }
  1035.  
  1036.     // test for number: decimal (123), octal (0123), or hexadecimal (0x123)
  1037.  
  1038.     if (isdigit((unsigned char)c)) {
  1039.         sint64 v = 0;
  1040.  
  1041.         if (c=='0' && tokstr[0] == 'x') {
  1042.  
  1043.             // hex (base 16)
  1044.             ++tokstr;
  1045.  
  1046.             while(isxdigit((unsigned char)(c = *tokstr++))) {
  1047.                 v = v*16 + (strchr(hexdig, toupper(c))-hexdig);
  1048.             }
  1049.  
  1050.         } else if (c=='0' && isdigit((unsigned char)tokstr[0])) {
  1051.             // octal (base 8)
  1052.             while((c=*tokstr++)>='0' && c<='7')
  1053.                 v = v*8 + (c-'0');
  1054.         } else {
  1055.             // check for float
  1056.             const char *s = tokstr;
  1057.             while(*s) {
  1058.                 if (*s == '.' || *s == 'e' || *s == 'E') {
  1059.                     // It's a float -- parse and return.
  1060.  
  1061.                     --tokstr;
  1062.                     tokdval = strtod(tokstr, (char **)&tokstr);
  1063.                     return TOK_DBLVAL;
  1064.                 }
  1065.  
  1066.                 if (!isdigit((unsigned char)*s))
  1067.                     break;
  1068.                 ++s;
  1069.             }
  1070.  
  1071.             // decimal
  1072.             v = (c-'0');
  1073.             while(isdigit((unsigned char)(c=*tokstr++)))
  1074.                 v = v*10 + (c-'0');
  1075.         }
  1076.         --tokstr;
  1077.  
  1078.         if (v > 0x7FFFFFFF) {
  1079.             toklval = v;
  1080.             return TOK_LONGVAL;
  1081.         } else {
  1082.             tokival = (int)v;
  1083.             return TOK_INTVAL;
  1084.         }
  1085.     }
  1086.  
  1087.     // test for symbols:
  1088.     //
  1089.     //    charset:    +-*/<>=!&|^[]~;%(),
  1090.     //    solitary:    +-*/<>=!&|^[]~;%(),
  1091.     //    extra:        != <= >= == && ||
  1092.     //
  1093.     //    the '/' is handled above for comment handling
  1094.  
  1095.     if (c=='!')
  1096.         if (tokstr[0] == '=') { ++tokstr; return TOK_NOTEQ;  } else return '!';
  1097.  
  1098.     if (c=='<')
  1099.         if (tokstr[0] == '=') { ++tokstr; return TOK_LESSEQ; } else return '<';
  1100.  
  1101.     if (c=='>')
  1102.         if (tokstr[0] == '=') { ++tokstr; return TOK_GRTREQ; } else return '>';
  1103.  
  1104.     if (c=='=')
  1105.         if (tokstr[0] == '=') { ++tokstr; return TOK_EQUALS; } else return '=';
  1106.  
  1107.     if (c=='&')
  1108.         if (tokstr[0] == '&') { ++tokstr; return TOK_AND;    } else return '&';
  1109.  
  1110.     if (c=='|')
  1111.         if (tokstr[0] == '|') { ++tokstr; return TOK_OR;     } else return '|';
  1112.  
  1113.     if (strchr("+-*^[]~;%(),.",c))
  1114.         return c;
  1115.  
  1116.     SCRIPT_ERROR(PARSE_ERROR);
  1117. }
  1118.  
  1119. void VDScriptInterpreter::GC() {
  1120.     strheap.BeginGC();
  1121.     vartbl.MarkStrings(strheap);
  1122.     strheap.EndGC();
  1123. }
  1124.