diff options
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | pygments/lexers/_mapping.py | 1 | ||||
-rw-r--r-- | pygments/lexers/_vbscript_builtins.py | 275 | ||||
-rw-r--r-- | pygments/lexers/basic.py | 70 | ||||
-rw-r--r-- | tests/examplefiles/example.vbs | 55 | ||||
-rw-r--r-- | tests/test_basic.py | 74 |
6 files changed, 474 insertions, 4 deletions
@@ -7,7 +7,8 @@ Other contributors, listed alphabetically, are: * Sam Aaron -- Ioke lexer * Ali Afshar -- image formatter -* Thomas Aglassinger -- Easytrieve, JCL, Rexx and Transact-SQL lexers +* Thomas Aglassinger -- Easytrieve, JCL, Rexx, Transact-SQL and VBScript + lexers * Muthiah Annamalai -- Ezhil lexer * Kumar Appaiah -- Debian control lexer * Andreas Amann -- AppleScript lexer diff --git a/pygments/lexers/_mapping.py b/pygments/lexers/_mapping.py index a6097b1c..882ec292 100644 --- a/pygments/lexers/_mapping.py +++ b/pygments/lexers/_mapping.py @@ -422,6 +422,7 @@ LEXERS = { 'TypoScriptHtmlDataLexer': ('pygments.lexers.typoscript', 'TypoScriptHtmlData', ('typoscripthtmldata',), (), ()), 'TypoScriptLexer': ('pygments.lexers.typoscript', 'TypoScript', ('typoscript',), ('*.ts', '*.txt'), ('text/x-typoscript',)), 'UrbiscriptLexer': ('pygments.lexers.urbi', 'UrbiScript', ('urbiscript',), ('*.u',), ('application/x-urbiscript',)), + 'VBScriptLexer': ('pygments.lexers.basic', 'VBScript', (), ('*.vbs', '*.VBS'), ()), 'VCLLexer': ('pygments.lexers.varnish', 'VCL', ('vcl',), ('*.vcl',), ('text/x-vclsrc',)), 'VCLSnippetLexer': ('pygments.lexers.varnish', 'VCLSnippets', ('vclsnippets', 'vclsnippet'), (), ('text/x-vclsnippet',)), 'VCTreeStatusLexer': ('pygments.lexers.console', 'VCTreeStatus', ('vctreestatus',), (), ()), diff --git a/pygments/lexers/_vbscript_builtins.py b/pygments/lexers/_vbscript_builtins.py new file mode 100644 index 00000000..493e5c7c --- /dev/null +++ b/pygments/lexers/_vbscript_builtins.py @@ -0,0 +1,275 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers._vbscript_builtins + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + These are manually translated lists from + http://www.indusoft.com/pdf/VBScript%20Reference.pdf. + + :copyright: Copyright 2006-2015 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +KEYWORDS = [ + # dim: special rule + 'call', + 'case', + 'class', + # const: special rule + 'do', + 'each', + 'else', + 'elseif', + 'end', + 'erase', + 'execute', + 'function', + 'exit', + 'for', + 'function', + 'GetRef', + 'global', + 'if', + 'let', + 'loop', + 'next', + 'new', + # option: special rule + 'private', + 'public', + 'redim', + 'select', + 'set', + 'sub', + 'then', + 'wend', + 'while', + 'with', +] + +BUILTIN_FUNCTIONS = [ + 'Abs', + 'Array', + 'Asc', + 'Atn', + 'CBool', + 'CByte', + 'CCur', + 'CDate', + 'CDbl', + 'Chr', + 'CInt', + 'CLng', + 'Cos', + 'CreateObject', + 'CSng', + 'CStr', + 'Date', + 'DateAdd', + 'DateDiff', + 'DatePart', + 'DateSerial', + 'DateValue', + 'Day', + 'Eval', + 'Exp', + 'Filter', + 'Fix', + 'FormatCurrency', + 'FormatDateTime', + 'FormatNumber', + 'FormatPercent', + 'GetObject', + 'GetLocale', + 'Hex', + 'Hour', + 'InStr', + 'inStrRev', + 'Int', + 'IsArray', + 'IsDate', + 'IsEmpty', + 'IsNull', + 'IsNumeric', + 'IsObject', + 'Join', + 'LBound', + 'LCase', + 'Left', + 'Len', + 'LoadPicture', + 'Log', + 'LTrim', + 'Mid', + 'Minute', + 'Month', + 'MonthName', + 'MsgBox', + 'Now', + 'Oct', + 'Randomize', + 'RegExp', + 'Replace', + 'RGB', + 'Right', + 'Rnd', + 'Round', + 'RTrim', + 'ScriptEngine', + 'ScriptEngineBuildVersion', + 'ScriptEngineMajorVersion', + 'ScriptEngineMinorVersion', + 'Second', + 'SetLocale', + 'Sgn', + 'Space', + 'Split', + 'Sqr', + 'StrComp', + 'String', + 'StrReverse', + 'Tan', + 'Time', + 'Timer', + 'TimeSerial', + 'TimeValue', + 'Trim', + 'TypeName', + 'UBound', + 'UCase', + 'VarType', + 'Weekday', + 'WeekdayName', + 'Year', +] + +BUILTIN_VARIABLES = [ + 'Debug', + 'Dictionary', + 'Drive', + 'Drives', + 'Err', + 'File', + 'Files', + 'FileSystemObject', + 'Folder', + 'Folders', + 'Match', + 'Matches', + 'RegExp', + 'Submatches', + 'TextStream', +] + +OPERATORS = [ + '+', + '-', + '*', + '/', + '\\', + '^', + '|', + '<', + '<=', + '>', + '>=', + '=', + '<>', + '&', + '$', +] + +OPERATOR_WORDS = [ + 'mod', + 'and', + 'or', + 'xor', + 'eqv', + 'imp', + 'is', + 'not', +] + +BUILTIN_CONSTANTS = [ + 'vbAbort', + 'vbAbortRetryIgnore', + 'vbApplicationModal', + 'vbArray', + 'vbBinaryCompare', + 'vbBlack', + 'vbBlue', + 'vbBoole', + 'vbByte', + 'vbCancel', + 'vbCr', + 'vbCritical', + 'vbCrLf', + 'vbCurrency', + 'vbCyan', + 'vbDataObject', + 'vbDate', + 'vbDefaultButton1', + 'vbDefaultButton2', + 'vbDefaultButton3', + 'vbDefaultButton4', + 'vbDouble', + 'vbEmpty', + 'vbError', + 'vbExclamation', + 'vbFalse', + 'vbFirstFullWeek', + 'vbFirstJan1', + 'vbFormFeed', + 'vbFriday', + 'vbGeneralDate', + 'vbGreen', + 'vbIgnore', + 'vbInformation', + 'vbInteger', + 'vbLf', + 'vbLong', + 'vbLongDate', + 'vbLongTime', + 'vbMagenta', + 'vbMonday', + 'vbMsgBoxHelpButton', + 'vbMsgBoxRight', + 'vbMsgBoxRtlReading', + 'vbMsgBoxSetForeground', + 'vbNewLine', + 'vbNo', + 'vbNull', + 'vbNullChar', + 'vbNullString', + 'vbObject', + 'vbObjectError', + 'vbOK', + 'vbOKCancel', + 'vbOKOnly', + 'vbQuestion', + 'vbRed', + 'vbRetry', + 'vbRetryCancel', + 'vbSaturday', + 'vbShortDate', + 'vbShortTime', + 'vbSingle', + 'vbString', + 'vbSunday', + 'vbSystemModal', + 'vbTab', + 'vbTextCompare', + 'vbThursday', + 'vbTrue', + 'vbTuesday', + 'vbUseDefault', + 'vbUseSystem', + 'vbUseSystem', + 'vbVariant', + 'vbVerticalTab', + 'vbWednesday', + 'vbWhite', + 'vbYellow', + 'vbYes', + 'vbYesNo', + 'vbYesNoCancel', +]
\ No newline at end of file diff --git a/pygments/lexers/basic.py b/pygments/lexers/basic.py index a73ad8b4..09d9bfb9 100644 --- a/pygments/lexers/basic.py +++ b/pygments/lexers/basic.py @@ -12,11 +12,13 @@ import re from pygments.lexer import RegexLexer, bygroups, default, words, include -from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ - Number, Punctuation +from pygments.token import Comment, Error, Keyword, Name, Number, \ + Punctuation, Operator, String, Text, Whitespace +from pygments.lexers import _vbscript_builtins + __all__ = ['BlitzBasicLexer', 'BlitzMaxLexer', 'MonkeyLexer', 'CbmBasicV2Lexer', - 'QBasicLexer'] + 'QBasicLexer', 'VBScriptLexer'] class BlitzMaxLexer(RegexLexer): @@ -498,3 +500,65 @@ class QBasicLexer(RegexLexer): def analyse_text(text): if '$DYNAMIC' in text or '$STATIC' in text: return 0.9 + + +class VBScriptLexer(RegexLexer): + """ + VBScript is scripting language that is modeled on Visual Basic. + + .. versionadded:: 2.2 + """ + name = 'VBScript' + aliases = [] + filenames = ['*.vbs', '*.VBS'] + flags = re.IGNORECASE + + tokens = { + 'root': [ + (r"'[^\n]*", Comment.Single), + (r'\s+', Whitespace), + ('"', String.Double, 'string'), + ('&h[0-9a-f]+', Number.Hex), + # Float variant 1, for example: 1., 1.e2, 1.2e3 + (r'[0-9]+\.[0-9]*(e[+-]?[0-9]+)?', Number.Float), + (r'\.[0-9]+(e[+-]?[0-9]+)?', Number.Float), # Float variant 2, for example: .1, .1e2 + (r'[0-9]+e[+-]?[0-9]+', Number.Float), # Float variant 3, for example: 123e45 + ('\d+', Number.Integer), + ('#.+#', String), # date or time value + (r'(dim)(\s+)([a-z_][a-z0-9_]*)', + bygroups(Keyword.Declaration, Whitespace, Name.Variable), 'dim_more'), + (r'(function|sub)(\s+)([a-z_][a-z0-9_]*)', + bygroups(Keyword.Declaration, Whitespace, Name.Function)), + (r'(class)(\s+)([a-z_][a-z0-9_]*)', bygroups(Keyword.Declaration, Whitespace, Name.Class)), + (r'(const)(\s+)([a-z_][a-z0-9_]*)', bygroups(Keyword.Declaration, Whitespace, Name.Constant)), + (r'(end)(\s+)(class|function|if|property|sub|with)', bygroups(Keyword, Whitespace, Keyword)), + (r'(on)(\s+)(error)(\s+)(goto)(\s+)(0)', + bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword, Whitespace, Number.Integer)), + (r'(on)(\s+)(error)(\s+)(resume)(\s+)(next)', + bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword, Whitespace, Keyword)), + (r'(option)(\s+)(explicit)', bygroups(Keyword, Whitespace, Keyword)), + (r'(property)(\s+)(get|let|set)(\s+)([a-z_][a-z0-9_]*)', + bygroups(Keyword.Declaration, Whitespace, Keyword.Declaration, Whitespace, Name.Property)), + (r'rem\s.*[^\n]*', Comment.Single), + (words(_vbscript_builtins.KEYWORDS, suffix=r'\b'), Keyword), + (words(_vbscript_builtins.OPERATORS), Operator), + (words(_vbscript_builtins.OPERATOR_WORDS, suffix=r'\b'), Operator.Word), + (words(_vbscript_builtins.BUILTIN_CONSTANTS, suffix=r'\b'), Name.Constant), + (words(_vbscript_builtins.BUILTIN_FUNCTIONS, suffix=r'\b'), Name.Builtin), + (words(_vbscript_builtins.BUILTIN_VARIABLES, suffix=r'\b'), Name.Builtin), + (r'[a-z_][a-z0-9_]*', Name), + (r'\b_\n', Operator), + (words(r'(),.:'), Punctuation), + ('.+(\n)?', Error) + ], + 'dim_more': [ + (r'(\s*)(,)(\s*)([a-z_][a-z0-9]*)', bygroups(Whitespace, Punctuation, Whitespace, Name.Variable)), + default('#pop'), + ], + 'string': [ + (r'[^"\n]+', String.Double), + (r'\"\"', String.Double), + (r'"', String.Double, '#pop'), + (r'\n', Error, '#pop'), # Unterminated string + ], + }
\ No newline at end of file diff --git a/tests/examplefiles/example.vbs b/tests/examplefiles/example.vbs new file mode 100644 index 00000000..d962b73d --- /dev/null +++ b/tests/examplefiles/example.vbs @@ -0,0 +1,55 @@ +rem VBScript examples + +' Various constants of different types +const someText = "some " & """text""" +const someInt = 123 +const someHex = &h3110c0d3 +const someFloat = 123.45e-67 +const someDate = #1/2/2016# +const someTime = #12:34:56 AM# +const someBool = vbTrue ' -1 + +' Do some math. +radius = 1.e2 +area = radius ^ 2 * 3.1315 +a = 17 : b = 23 +c = sqr(a ^2 + b ^ 2) + +' Write 10 files. +For i = 1 to 10 + createFile( i ) +Next + +Public Sub createFile(a) + Dim fso, TargetFile + TargetPath = "C:\some_" & a & ".tmp" + Set fso = CreateObject("Scripting.FileSystemObject") + Set TargetFile = fso.CreateTextFile(TargetPath) + TargetFile.WriteLine("Hello " & vbCrLf & "world!") + TargetFile.Close +End Sub + +' Define a class with a property. +Class Customer + Private m_CustomerName + + Private Sub Class_Initialize + m_CustomerName = "" + End Sub + + ' CustomerName property. + Public Property Get CustomerName + CustomerName = m_CustomerName + End Property + + Public Property Let CustomerName(custname) + m_CustomerName = custname + End Property +End Class + +' Special constructs +Option Explicit +On Error Resume Next +On Error Goto 0 + +' Comment without terminating CR/LF.
\ No newline at end of file diff --git a/tests/test_basic.py b/tests/test_basic.py new file mode 100644 index 00000000..03d10cd2 --- /dev/null +++ b/tests/test_basic.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +""" + Pygments Basic lexers tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2006-2016 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import unittest + +from pygments.lexers.basic import VBScriptLexer +from pygments.token import Error, Name, Number, Punctuation, String, Whitespace + + +class VBScriptLexerTest(unittest.TestCase): + + def setUp(self): + self.lexer = VBScriptLexer() + + def _assert_are_tokens_of_type(self, examples, expected_token_type): + for test_number, example in enumerate(examples.split(), 1): + token_count = 0 + for token_type, token_value in self.lexer.get_tokens(example): + if token_type != Whitespace: + token_count += 1 + self.assertEqual( + token_type, expected_token_type, + 'token_type #%d for %s is be %s but must be %s' % + (test_number, token_value, token_type, expected_token_type)) + self.assertEqual( + token_count, 1, + '%s must yield exactly 1 token instead of %d' % + (example, token_count)) + + def _assert_tokens_match(self, text, expected_tokens_without_trailing_newline): + actual_tokens = tuple(self.lexer.get_tokens(text)) + if (len(actual_tokens) >= 1) and (actual_tokens[-1] == (Whitespace, '\n')): + actual_tokens = tuple(actual_tokens[:-1]) + self.assertEqual( + expected_tokens_without_trailing_newline, actual_tokens, + 'text must yield expected tokens: %s' % text) + + def test_can_lex_float(self): + self._assert_are_tokens_of_type( + '1. 1.e1 .1 1.2 1.2e3 1.2e+3 1.2e-3 1e2', Number.Float) + self._assert_tokens_match( + '1e2.1e2', + ((Number.Float, '1e2'), (Number.Float, '.1e2')) + ) + + def test_can_reject_almost_float(self): + self._assert_tokens_match( + '.e1', + ((Punctuation, '.'), (Name, 'e1'))) + + def test_can_lex_integer(self): + self._assert_are_tokens_of_type( + '1 23 456', Number.Integer) + + def test_can_lex_names(self): + self._assert_are_tokens_of_type( + u'thingy thingy123 _thingy _123', Name) + + def test_can_recover_after_unterminated_string(self): + self._assert_tokens_match( + '"x\nx', + ((String.Double, '"'), (String.Double, 'x'), (Error, '\n'), (Name, 'x')) + ) + + def test_can_recover_from_invalid_character(self): + self._assert_tokens_match( + 'a;bc\nd', + ((Name, 'a'), (Error, ';bc\n'), (Name, 'd')) + ) |