[PATCH 1/2] vbscript: Allow most keywords to be used as 'dot' identifiers.

Brendan McGrath brendan at redmandi.com
Wed Feb 27 00:05:49 CST 2019


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46318
Signed-off-by: Brendan McGrath <brendan at redmandi.com>
---
The added keywords are based on the documentation found at:
https://rosettacode.org/wiki/BNF_Grammar#VBScript

There appears to be special code for the 'rem' keyword, so I couldn't
get that to work (there's a TODO test in the next patch if it's wanted).

That page also listed the following keywords (which we don't have):
- Erase
- Preserve
- Redim
- With

But did not mention these (which we do have):
- Stop
- Me

I'm not sure how accurate that site is so I've added a test for
every keyword I added to make sure they work on Windows.

 dlls/vbscript/parser.y       |  86 +++++++++++++++++----
 dlls/vbscript/tests/lang.vbs |  55 +++++++++++++
 dlls/vbscript/tests/run.c    | 144 ++++++++++++++++++++++++++++++++---
 3 files changed, 258 insertions(+), 27 deletions(-)

diff --git a/dlls/vbscript/parser.y b/dlls/vbscript/parser.y
index b81d7919662..52c98fca596 100644
--- a/dlls/vbscript/parser.y
+++ b/dlls/vbscript/parser.y
@@ -102,20 +102,21 @@ static statement_t *link_statements(statement_t*,statement_t*);
     double dbl;
 }
 
-%token tEOF tNL tREM tEMPTYBRACKETS
-%token tTRUE tFALSE
-%token tNOT tAND tOR tXOR tEQV tIMP tNEQ
-%token tIS tLTEQ tGTEQ tMOD
-%token tCALL tDIM tSUB tFUNCTION tGET tLET tCONST
-%token tIF tELSE tELSEIF tEND tTHEN tEXIT
-%token tWHILE tWEND tDO tLOOP tUNTIL tFOR tTO tEACH tIN
-%token tSELECT tCASE
-%token tBYREF tBYVAL
-%token tOPTION
-%token tSTOP
-%token tNOTHING tEMPTY tNULL
-%token tCLASS tSET tNEW tPUBLIC tPRIVATE tME
-%token tNEXT tON tRESUME tGOTO
+%token tEOF tNL tEMPTYBRACKETS
+%token tLTEQ tGTEQ tNEQ
+%token tSTOP tME tREM
+%token <string> tTRUE tFALSE
+%token <string> tNOT tAND tOR tXOR tEQV tIMP
+%token <string> tIS tMOD
+%token <string> tCALL tDIM tSUB tFUNCTION tGET tLET tCONST
+%token <string> tIF tELSE tELSEIF tEND tTHEN tEXIT
+%token <string> tWHILE tWEND tDO tLOOP tUNTIL tFOR tTO tEACH tIN
+%token <string> tSELECT tCASE
+%token <string> tBYREF tBYVAL
+%token <string> tOPTION
+%token <string> tNOTHING tEMPTY tNULL
+%token <string> tCLASS tSET tNEW tPUBLIC tPRIVATE
+%token <string> tNEXT tON tRESUME tGOTO
 %token <string> tIdentifier tString
 %token <string> tDEFAULT tERROR tEXPLICIT tPROPERTY tSTEP
 %token <lng> tLong tShort
@@ -137,7 +138,7 @@ static statement_t *link_statements(statement_t*,statement_t*);
 %type <dim_decl> DimDeclList DimDecl
 %type <dim_list> DimList
 %type <const_decl> ConstDecl ConstDeclList
-%type <string> Identifier
+%type <string> Identifier DotIdentifier
 %type <case_clausule> CaseClausules
 
 %%
@@ -209,7 +210,7 @@ SimpleStatement
 
 MemberExpression
     : Identifier                            { $$ = new_member_expression(ctx, NULL, $1); CHECK_ERROR; }
-    | CallExpression '.' Identifier         { $$ = new_member_expression(ctx, $1, $3); CHECK_ERROR; }
+    | CallExpression '.' DotIdentifier      { $$ = new_member_expression(ctx, $1, $3); CHECK_ERROR; }
 
 DimDeclList
     : DimDecl                               { $$ = $1; }
@@ -451,6 +452,59 @@ Identifier
     | tPROPERTY      { $$ = $1; }
     | tSTEP          { $$ = $1; }
 
+/* most keywords can be an identifier after a dot */
+DotIdentifier
+    : Identifier     { $$ = $1; }
+    | tTRUE          { $$ = $1; }
+    | tFALSE         { $$ = $1; }
+    | tNOT           { $$ = $1; }
+    | tAND           { $$ = $1; }
+    | tOR            { $$ = $1; }
+    | tXOR           { $$ = $1; }
+    | tEQV           { $$ = $1; }
+    | tIMP           { $$ = $1; }
+    | tIS            { $$ = $1; }
+    | tMOD           { $$ = $1; }
+    | tCALL          { $$ = $1; }
+    | tDIM           { $$ = $1; }
+    | tSUB           { $$ = $1; }
+    | tFUNCTION      { $$ = $1; }
+    | tGET           { $$ = $1; }
+    | tLET           { $$ = $1; }
+    | tCONST         { $$ = $1; }
+    | tIF            { $$ = $1; }
+    | tELSE          { $$ = $1; }
+    | tELSEIF        { $$ = $1; }
+    | tEND           { $$ = $1; }
+    | tTHEN          { $$ = $1; }
+    | tEXIT          { $$ = $1; }
+    | tWHILE         { $$ = $1; }
+    | tWEND          { $$ = $1; }
+    | tDO            { $$ = $1; }
+    | tLOOP          { $$ = $1; }
+    | tUNTIL         { $$ = $1; }
+    | tFOR           { $$ = $1; }
+    | tTO            { $$ = $1; }
+    | tEACH          { $$ = $1; }
+    | tIN            { $$ = $1; }
+    | tSELECT        { $$ = $1; }
+    | tCASE          { $$ = $1; }
+    | tBYREF         { $$ = $1; }
+    | tBYVAL         { $$ = $1; }
+    | tOPTION        { $$ = $1; }
+    | tNOTHING       { $$ = $1; }
+    | tEMPTY         { $$ = $1; }
+    | tNULL          { $$ = $1; }
+    | tCLASS         { $$ = $1; }
+    | tSET           { $$ = $1; }
+    | tNEW           { $$ = $1; }
+    | tPUBLIC        { $$ = $1; }
+    | tPRIVATE       { $$ = $1; }
+    | tNEXT          { $$ = $1; }
+    | tON            { $$ = $1; }
+    | tRESUME        { $$ = $1; }
+    | tGOTO          { $$ = $1; }
+
 /* Most statements accept both new line and ':' as separators */
 StSep
     : tNL
diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs
index 15ad84a23ac..098506402bc 100644
--- a/dlls/vbscript/tests/lang.vbs
+++ b/dlls/vbscript/tests/lang.vbs
@@ -1365,4 +1365,59 @@ sub test_identifiers
 end sub
 call test_identifiers()
 
+sub test_dotIdentifiers
+    ' test keywords that can also be an indentifier after a dot
+    ' Call ok(testObj.rem = 10, "testObj.rem = " & testObj.rem & " expected 10")
+    Call ok(testObj.true = 10, "testObj.true = " & testObj.true & " expected 10")
+    Call ok(testObj.false = 10, "testObj.false = " & testObj.false & " expected 10")
+    Call ok(testObj.not = 10, "testObj.not = " & testObj.not & " expected 10")
+    Call ok(testObj.and = 10, "testObj.and = " & testObj.and & " expected 10")
+    Call ok(testObj.or = 10, "testObj.or = " & testObj.or & " expected 10")
+    Call ok(testObj.xor = 10, "testObj.xor = " & testObj.xor & " expected 10")
+    Call ok(testObj.eqv = 10, "testObj.eqv = " & testObj.eqv & " expected 10")
+    Call ok(testObj.imp = 10, "testObj.imp = " & testObj.imp & " expected 10")
+    Call ok(testObj.is = 10, "testObj.is = " & testObj.is & " expected 10")
+    Call ok(testObj.mod = 10, "testObj.mod = " & testObj.mod & " expected 10")
+    Call ok(testObj.call = 10, "testObj.call = " & testObj.call & " expected 10")
+    Call ok(testObj.dim = 10, "testObj.dim = " & testObj.dim & " expected 10")
+    Call ok(testObj.sub = 10, "testObj.sub = " & testObj.sub & " expected 10")
+    Call ok(testObj.function = 10, "testObj.function = " & testObj.function & " expected 10")
+    Call ok(testObj.get = 10, "testObj.get = " & testObj.get & " expected 10")
+    Call ok(testObj.let = 10, "testObj.let = " & testObj.let & " expected 10")
+    Call ok(testObj.const = 10, "testObj.const = " & testObj.const & " expected 10")
+    Call ok(testObj.if = 10, "testObj.if = " & testObj.if & " expected 10")
+    Call ok(testObj.else = 10, "testObj.else = " & testObj.else & " expected 10")
+    Call ok(testObj.elseif = 10, "testObj.elseif = " & testObj.elseif & " expected 10")
+    Call ok(testObj.end = 10, "testObj.end = " & testObj.end & " expected 10")
+    Call ok(testObj.then = 10, "testObj.then = " & testObj.then & " expected 10")
+    Call ok(testObj.exit = 10, "testObj.exit = " & testObj.exit & " expected 10")
+    Call ok(testObj.while = 10, "testObj.while = " & testObj.while & " expected 10")
+    Call ok(testObj.wend = 10, "testObj.wend = " & testObj.wend & " expected 10")
+    Call ok(testObj.do = 10, "testObj.do = " & testObj.do & " expected 10")
+    Call ok(testObj.loop = 10, "testObj.loop = " & testObj.loop & " expected 10")
+    Call ok(testObj.until = 10, "testObj.until = " & testObj.until & " expected 10")
+    Call ok(testObj.for = 10, "testObj.for = " & testObj.for & " expected 10")
+    Call ok(testObj.to = 10, "testObj.to = " & testObj.to & " expected 10")
+    Call ok(testObj.each = 10, "testObj.each = " & testObj.each & " expected 10")
+    Call ok(testObj.in = 10, "testObj.in = " & testObj.in & " expected 10")
+    Call ok(testObj.select = 10, "testObj.select = " & testObj.select & " expected 10")
+    Call ok(testObj.case = 10, "testObj.case = " & testObj.case & " expected 10")
+    Call ok(testObj.byref = 10, "testObj.byref = " & testObj.byref & " expected 10")
+    Call ok(testObj.byval = 10, "testObj.byval = " & testObj.byval & " expected 10")
+    Call ok(testObj.option = 10, "testObj.option = " & testObj.option & " expected 10")
+    Call ok(testObj.nothing = 10, "testObj.nothing = " & testObj.nothing & " expected 10")
+    Call ok(testObj.empty = 10, "testObj.empty = " & testObj.empty & " expected 10")
+    Call ok(testObj.null = 10, "testObj.null = " & testObj.null & " expected 10")
+    Call ok(testObj.class = 10, "testObj.class = " & testObj.class & " expected 10")
+    Call ok(testObj.set = 10, "testObj.set = " & testObj.set & " expected 10")
+    Call ok(testObj.new = 10, "testObj.new = " & testObj.new & " expected 10")
+    Call ok(testObj.public = 10, "testObj.public = " & testObj.public & " expected 10")
+    Call ok(testObj.private = 10, "testObj.private = " & testObj.private & " expected 10")
+    Call ok(testObj.next = 10, "testObj.next = " & testObj.next & " expected 10")
+    Call ok(testObj.on = 10, "testObj.on = " & testObj.on & " expected 10")
+    Call ok(testObj.resume = 10, "testObj.resume = " & testObj.resume & " expected 10")
+    Call ok(testObj.goto = 10, "testObj.goto = " & testObj.goto & " expected 10")
+end sub
+'call test_dotIdentifiers
+
 reportSuccess()
diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c
index 191f5a79a0d..a28cf74196b 100644
--- a/dlls/vbscript/tests/run.c
+++ b/dlls/vbscript/tests/run.c
@@ -58,6 +58,9 @@ extern const CLSID CLSID_VBScriptRegExp;
 #define SET_EXPECT(func) \
     expect_ ## func = TRUE
 
+#define REF_EXPECT(func) \
+    (&expect_ ## func), (&called_ ## func)
+
 #define CHECK_EXPECT2(func) \
     do { \
         ok(expect_ ##func, "unexpected call " #func "\n"); \
@@ -127,6 +130,56 @@ DEFINE_EXPECT(OnScriptError);
 
 #define DISPID_TESTOBJ_PROPGET      2000
 #define DISPID_TESTOBJ_PROPPUT      2001
+#define DISPID_TESTOBJ_REM          2002
+#define DISPID_TESTOBJ_TRUE         2003
+#define DISPID_TESTOBJ_FALSE        2004
+#define DISPID_TESTOBJ_NOT          2005
+#define DISPID_TESTOBJ_AND          2006
+#define DISPID_TESTOBJ_OR           2007
+#define DISPID_TESTOBJ_XOR          2008
+#define DISPID_TESTOBJ_EQV          2009
+#define DISPID_TESTOBJ_IMP          2010
+#define DISPID_TESTOBJ_IS           2011
+#define DISPID_TESTOBJ_MOD          2012
+#define DISPID_TESTOBJ_CALL         2013
+#define DISPID_TESTOBJ_DIM          2014
+#define DISPID_TESTOBJ_SUB          2015
+#define DISPID_TESTOBJ_FUNCTION     2016
+#define DISPID_TESTOBJ_GET          2017
+#define DISPID_TESTOBJ_LET          2018
+#define DISPID_TESTOBJ_CONST        2019
+#define DISPID_TESTOBJ_IF           2020
+#define DISPID_TESTOBJ_ELSE         2021
+#define DISPID_TESTOBJ_ELSEIF       2022
+#define DISPID_TESTOBJ_END          2023
+#define DISPID_TESTOBJ_THEN         2024
+#define DISPID_TESTOBJ_EXIT         2025
+#define DISPID_TESTOBJ_WHILE        2026
+#define DISPID_TESTOBJ_WEND         2027
+#define DISPID_TESTOBJ_DO           2028
+#define DISPID_TESTOBJ_LOOP         2029
+#define DISPID_TESTOBJ_UNTIL        2030
+#define DISPID_TESTOBJ_FOR          2031
+#define DISPID_TESTOBJ_TO           2032
+#define DISPID_TESTOBJ_EACH         2033
+#define DISPID_TESTOBJ_IN           2034
+#define DISPID_TESTOBJ_SELECT       2035
+#define DISPID_TESTOBJ_CASE         2036
+#define DISPID_TESTOBJ_BYREF        2037
+#define DISPID_TESTOBJ_BYVAL        2038
+#define DISPID_TESTOBJ_OPTION       2039
+#define DISPID_TESTOBJ_NOTHING      2040
+#define DISPID_TESTOBJ_EMPTY        2041
+#define DISPID_TESTOBJ_NULL         2042
+#define DISPID_TESTOBJ_CLASS        2043
+#define DISPID_TESTOBJ_SET          2044
+#define DISPID_TESTOBJ_NEW          2045
+#define DISPID_TESTOBJ_PUBLIC       2046
+#define DISPID_TESTOBJ_PRIVATE      2047
+#define DISPID_TESTOBJ_NEXT         2048
+#define DISPID_TESTOBJ_ON           2049
+#define DISPID_TESTOBJ_RESUME       2050
+#define DISPID_TESTOBJ_GOTO         2051
 
 #define DISPID_COLLOBJ_RESET        3000
 
@@ -756,17 +809,80 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc
 
 static HRESULT WINAPI testObj_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
 {
-    if(!strcmp_wa(bstrName, "propget")) {
-        CHECK_EXPECT(testobj_propget_d);
-        test_grfdex(grfdex, fdexNameCaseInsensitive);
-        *pid = DISPID_TESTOBJ_PROPGET;
-        return S_OK;
-    }
-    if(!strcmp_wa(bstrName, "propput")) {
-        CHECK_EXPECT(testobj_propput_d);
-        test_grfdex(grfdex, fdexNameCaseInsensitive);
-        *pid = DISPID_TESTOBJ_PROPPUT;
-        return S_OK;
+    typedef struct {
+          const char * const name;
+          DISPID pid;
+          BOOL *expect;
+          BOOL *called;
+    } dispid_t;
+
+    dispid_t dispids[] = {
+       { "propget", DISPID_TESTOBJ_PROPGET, REF_EXPECT(testobj_propget_d) },
+       { "propput", DISPID_TESTOBJ_PROPPUT, REF_EXPECT(testobj_propput_d) },
+       { "rem", DISPID_TESTOBJ_REM, NULL },
+       { "true", DISPID_TESTOBJ_TRUE, NULL },
+       { "false", DISPID_TESTOBJ_FALSE, NULL },
+       { "not", DISPID_TESTOBJ_NOT, NULL },
+       { "and", DISPID_TESTOBJ_AND, NULL },
+       { "or", DISPID_TESTOBJ_OR, NULL },
+       { "xor", DISPID_TESTOBJ_XOR, NULL },
+       { "eqv", DISPID_TESTOBJ_EQV, NULL },
+       { "imp", DISPID_TESTOBJ_IMP, NULL },
+       { "is", DISPID_TESTOBJ_IS, NULL },
+       { "mod", DISPID_TESTOBJ_MOD, NULL },
+       { "call", DISPID_TESTOBJ_CALL, NULL },
+       { "dim", DISPID_TESTOBJ_DIM, NULL },
+       { "sub", DISPID_TESTOBJ_SUB, NULL },
+       { "function", DISPID_TESTOBJ_FUNCTION, NULL },
+       { "get", DISPID_TESTOBJ_GET, NULL },
+       { "let", DISPID_TESTOBJ_LET, NULL },
+       { "const", DISPID_TESTOBJ_CONST, NULL },
+       { "if", DISPID_TESTOBJ_IF, NULL },
+       { "else", DISPID_TESTOBJ_ELSE, NULL },
+       { "elseif", DISPID_TESTOBJ_ELSEIF, NULL },
+       { "end", DISPID_TESTOBJ_END, NULL },
+       { "then", DISPID_TESTOBJ_THEN, NULL },
+       { "exit", DISPID_TESTOBJ_EXIT, NULL },
+       { "while", DISPID_TESTOBJ_WHILE, NULL },
+       { "wend", DISPID_TESTOBJ_WEND, NULL },
+       { "do", DISPID_TESTOBJ_DO, NULL },
+       { "loop", DISPID_TESTOBJ_LOOP, NULL },
+       { "until", DISPID_TESTOBJ_UNTIL, NULL },
+       { "for", DISPID_TESTOBJ_FOR, NULL },
+       { "to", DISPID_TESTOBJ_TO, NULL },
+       { "each", DISPID_TESTOBJ_EACH, NULL },
+       { "in", DISPID_TESTOBJ_IN, NULL },
+       { "select", DISPID_TESTOBJ_SELECT, NULL },
+       { "case", DISPID_TESTOBJ_CASE, NULL },
+       { "byref", DISPID_TESTOBJ_BYREF, NULL },
+       { "byval", DISPID_TESTOBJ_BYVAL, NULL },
+       { "option", DISPID_TESTOBJ_OPTION, NULL },
+       { "nothing", DISPID_TESTOBJ_NOTHING, NULL },
+       { "empty", DISPID_TESTOBJ_EMPTY, NULL },
+       { "null", DISPID_TESTOBJ_NULL, NULL },
+       { "class", DISPID_TESTOBJ_CLASS, NULL },
+       { "set", DISPID_TESTOBJ_SET, NULL },
+       { "new", DISPID_TESTOBJ_NEW, NULL },
+       { "public", DISPID_TESTOBJ_PUBLIC, NULL },
+       { "private", DISPID_TESTOBJ_PRIVATE, NULL },
+       { "next", DISPID_TESTOBJ_NEXT, NULL },
+       { "on", DISPID_TESTOBJ_ON, NULL },
+       { "resume", DISPID_TESTOBJ_RESUME, NULL },
+       { "goto", DISPID_TESTOBJ_GOTO, NULL },
+    };
+
+    for (int i = 0; i < ARRAY_SIZE(dispids); i++) {
+        if(!strcmp_wa(bstrName, dispids[i].name)) {
+            dispid_t *d = &dispids[i];
+            if(d->expect) {
+               ok(*d->expect, "unexpected call %s\n", d->name);
+               *d->called = TRUE;
+               *d->expect = FALSE;
+            }
+            test_grfdex(grfdex, fdexNameCaseInsensitive);
+            *pid = d->pid;
+            return S_OK;
+        }
     }
 
     ok(0, "unexpected call %s\n", wine_dbgstr_w(bstrName));
@@ -833,6 +949,12 @@ static HRESULT WINAPI testObj_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid,
         return S_OK;
     }
 
+    if (id >= DISPID_TESTOBJ_REM && id <= DISPID_TESTOBJ_GOTO) {
+        V_VT(pvarRes) = VT_I2;
+        V_I2(pvarRes) = 10;
+        return S_OK;
+    }
+
     ok(0, "unexpected call %d\n", id);
     return E_FAIL;
 }
-- 
2.17.1




More information about the wine-devel mailing list