VBScript Trivia: Bracket Identifiers and Reserved Word Incompatibilities
I want to spend some time addressing the security implications of private reflection in response to recent comments, but that is a big topic that will take some time, and work is absolutely crazy busy right now. Design Change Request Season opens today -- our last chance to get approved design changes (as opposed to mere bug fixes) into Whidbey before the betas go out. And as I'm on holiday at the end of the month, there's a lot to do in little time.
Therefore, I might not get to it soon. Today, I've got something to get off my chest.
I got a question yesterday from someone who was trying to parse a config file. He was confused by a typo. The code was supposed to say
If LineText = "[Global]" Then
The typo was that he forgot the quotes
If LineText = [Global] Then
OK, that's easy mistake to make when you're typing fast, you get a parse error, go to that line, put the quotes in, right? Except here's the confusing part: this code compiled and ran. Incorrectly, obviously, but it ran. What the heck?
Indeed, that's perfectly legal. In VBScript, here are the rules for what makes a legal identifier:
IDENTIFIER : BRACKETIDENTIFIER or NORMALIDENTIFIER
NORMALIDENTIFIER : LETTER followed by 0 to 254 IDCHARs, but not a RESERVEDWORD
BRACKETIDENTIFIER : [ 0 to 255 BRACKETCHARs ]
LETTER : a-z, A-Z
IDCHAR : a-z, A-Z, 0-9, _
BRACKETCHAR : any Unicode character except newline, linefeed, the null string terminator and ]
RESERVEDWORD : And As Boolean ByRef Byte ByVal Call Case Class Const Currency Debug Dim Do Double Each Else ElseIf Empty End EndIf Enum Eqv Event Exit False For Function Get Goto If Imp Implements In Integer Is Let Like Long Loop LSet Me Mod New Next Not Nothing Null On Option Optional Or ParamArray Preserve Private Public RaiseEvent ReDim Rem Resume RSet Select Set Shared Single Static Stop Sub Then To True Type TypeOf Until Variant WEnd While With Xor, reserved words are case-insensitive
That bracket syntax is kind of weird, isn't it? One reason to enable this syntax is so that you can write programs that look like this:
[Hey, how're you doing dude? Working hard, or hardly working?] = [Ha, ha, ha, that's very witty Eric!]
There are also two good reasons.
First, it lets you name a method after a reserved word.
Function Previous()
End Function
Function Next()
End Function
Uh oh, that fails to compile. But aha,
Function [Next]()
is legal. This also lets you call methods on third-party objects which might be reserved words. Imagine the irksomeness if, say, IE had a method raiseEvent which you could then not call from VBScript. You never know what strange thing someone is going to put into an object model.
The other reason this is handy is because it enables developers who want to use characters which are not common in English in their identifiers. Accented characters, Chinese characters, etc.
The rules in JScript are slightly different, but I'll go into them another time.
Which brings me to what I wanted to get off my chest. While I'm on the subject of reserved words, this seems like a good time to publicly document something that has been irking me for a long time. We tried to make VBScript a subset of VB6, but unfortunately there are a few "gotchas" -- accidental differences which cause a particular VBScript program to not be a legal VB6 program. One of those gotchas is the fact that the reserved word sets are different. You'll notice above that VBScript reserves RaiseEvent, even though there is no RaiseEvent feature in VBScript. That's because (a) we anticipated that we might have to add it some day, and (b) "Dim RaiseEvent" isn't legal VB6, so it shouldn't be legal VBScript either.
There is only one word that goes "the other way" -- an identifier that is illegal in VBScript but legal in VB6 -- and that's "Class". I felt bad about doing that, but without reserving Class it is impossible to determine whether Class Foo means "call a method with one argument" or "introduce a new class" without creating enormous bogosity in the parser.
For the record, the words that are legal identifiers in VBScript but illegal in VB6 are:
Abs AddressOf Any Array Attribute CBool CByte CCur CDate CDec CDbl CDecl CInt Circle CLng Close CSng CStr CVar CVErr Date Decimal Declare DefBool DefByte DefCur DefDate DefDec DefDblDefInt DefLng DefObj DefSng DefStr DefVar DoEvents Erase FixFriend Global GoSub Input InputB Int LBound Len LenB LINEINPUT Local Lock Open Print PSet Put Return Scale Seek Sgn Spc String Tab UBound Unlock VB_Base VB_Control VB_Creatable VB_Customizable VB_Description VB_Exposed VB_Ext_KEY VB_HelpID VB_Invoke_Func VB_Invoke_Property VB_Invoke_PropertyPut VB_Invoke_PropertyPutRef VB_MemberFlags VB_Name VB_PredeclaredId VB_ProcData VB_TemplateDerived VB_VarDescription VB_VarHelpID VB_VarMemberFlags VB_VarProcData VB_UserMemId VB_VarUserMemId VB_GlobalNameSpace WithEvents Write
Once we failed to reserve them in VBScript 1.0, we couldn't reserve them at any subsequent time without risking breaking existing scripts, which we did not want to do unless there was a clear benefit (such as adding classes). Argh.
There are other VB6 incompatibilities, some of them truly obscure, that I'll document at a later date I'm sure.
Comments
Anonymous
June 10, 2004
Since when does CLASS in vbscript do nothing, I have programmed many ASP "business objects" using the Class SomeThing End Class, syntax.
Quite useful actually, wish MS did more with VBScript classes.Anonymous
June 10, 2004
I'm sorry, but I can't for the life of me figure out what you're talking about. Where did I say that "class" does nothing in VBScript?
Incidentally, I'm glad you find them useful. I designed and implemented that feature.Anonymous
June 10, 2004
I misread what you intended by the following:
"There is only one word that goes "the other way" -- an identifier that is illegal in VBScript but legal in VB6 -- and that's "Class"."
However, kudos to you for implimenting that feature, I once developed an entire [massive] web app using classic ASP and VBscript Classes.
Great Stuff!
The only gripe i ever had with that scenario was there was no way to conditionally include a file in IIS, and no way to gracefully handle the error if a class declaration/implimentation was duplicated (which would happen if there was dependencies)
Great stuff though man, thanks, I spent years using them :)Anonymous
June 10, 2004
Thanks -- I considered making class redefinition legal, but decided against it. Too many weird corner cases.
I agree that it is somewhat irksome that there is no way to make an idempotent include in ASP.
Anyway, what I meant was that in VB6, "Dim Class" is legal, but that is illegal in VBScript v5. That's the only undecorated identifier that I know of which is both legal in VB6 and illegal in VBScript.Anonymous
April 08, 2005
How do you access the "Me" pointer inside of a class in VBScript?Anonymous
April 08, 2005
You use "me".
I think I'm misunderstanding the question. Can you clarify?Anonymous
April 26, 2006
I think Andrew means me.name. It would be useful to get the instance name of a VBScript Class object, esp. for global error handling routines to show which instance has caused a problem.Anonymous
September 06, 2007
We on the C# team hate making breaking changes. As my colleague Neal called out in his article on theAnonymous
October 25, 2007
I find Classes in VBScript very usefull, but it has it's issues. Like if you defined a private attribute inside a class, you can't reference it, at less you define a public get property. But if you wan't preserve the encapsulation this isn't what you looking for to do. Some one, can explain this to me. ThanksAnonymous
November 17, 2009
So, how do you handle that case in VBScript when you've declared a class and your script runs a second time?